-
Notifications
You must be signed in to change notification settings - Fork 491
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
8347753: VetoableListDecorator doesn't accept its own sublists for bulk operations #1679
base: master
Are you sure you want to change the base?
Conversation
👋 Welcome back mstrauss! A progress list of the required criteria for merging this PR into |
❗ This change is not yet ready to be integrated. |
Webrevs
|
@@ -387,14 +387,37 @@ public int hashCode() { | |||
return list.hashCode(); | |||
} | |||
|
|||
private class VetoableSubListDecorator implements List<E> { | |||
/** | |||
* Returns the specified collection as an unmodifiable list that can safely be used in all bulk |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think it might be easier to create a defensive copy always?
In other words, can we guarantee that it is impossible for the user to create a convoluted code involving maybe two VetoableListDecorators
where the second one loops back the changes to the first one, however ridiculous that might sound?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The way I see it, the situation that erroneously triggers ConcurrentModificationException
only happens when VetoableListDecorator
accesses its own sublist:
try {
modCount++;
boolean ret = list.addAll(index, c); // --> c is its own sublist
...
Since modCount
is modified first, and the sublist refers back to the same modified modCount
, the exception occurs. It can't occur when we are dealing with another list (or a sublist of another list), since in this case there is no self-referential conflict.
The way ArrayList
circumvents this problem is by incrementing modCount
only after the operation is done, not before it has started; it doesn't create a defensive copy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for clarification! I'll try to come up with a test (and I think even if I succeed, it does not mean that your solution is not good, since the caller can always create a defensive copy themselves).
BTW, what were you doing to trigger this bug?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BTW, what were you doing to trigger this bug?
This happened when I was writing some tests for the other VetoableListDecorator
PRs...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The scenario I was referring to is impossible, I think.
One possible alternative is to create the defensive copy each time, this will save one extra pointer every time an iterator or a sublist gets created (these objects might be long lived). The code in this PR creates a copy in many (most?) cases anyway, and in my opinion, the memory is more precious resource that CPU cycles (i.e. using extra memory costs many more CPU cycles in garbage collection etc.), so please consider that.
- some minor suggestions
public void testAddAll_subList() { | ||
list.addAll(list.subList(0, 2)); | ||
assertSingleCall(new String[] {"foo", "bar"}, new int[] {4, 4}); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: also check that the list contains the newly added elements?
(here and in added tests that involve subList?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added checks for the list content in all modified tests.
I don't quite understand what you mean. Can you elaborate? |
What I mean is simply get rid of the extra pointer like |
Okay, I see. We can't do that, because a sublist is specified to be a live view onto the source list. After all, |
good point, thanks for pointing this out. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm
Passing a
VetoableListDecorator.subList()
to any of its bulk operations (addAll
,setAll
,removeAll
,retainAll
) throwsConcurrentModificationException
. The reason is that theVetoableListDecorator.modCount
field is incremented before the underlying list's bulk operation is invoked, which causes a mismatch when the sublist is interrogated by the bulk operation.However, simply updating the
modCount
field after the underlying list was modified also doesn't work, as in this case listeners can't see the correct value formodCount
in their callback. The fix is to make a defensive copy of the sublist before invoking the underlying list's bulk operation./reviewers 2
Progress
Issue
Reviewers
Reviewing
Using
git
Checkout this PR locally:
$ git fetch https://git.openjdk.org/jfx.git pull/1679/head:pull/1679
$ git checkout pull/1679
Update a local copy of the PR:
$ git checkout pull/1679
$ git pull https://git.openjdk.org/jfx.git pull/1679/head
Using Skara CLI tools
Checkout this PR locally:
$ git pr checkout 1679
View PR using the GUI difftool:
$ git pr show -t 1679
Using diff file
Download this PR as a diff file:
https://git.openjdk.org/jfx/pull/1679.diff
Using Webrev
Link to Webrev Comment