Skip to content

Conversation

@mstr2
Copy link
Collaborator

@mstr2 mstr2 commented Jan 15, 2025

Passing a VetoableListDecorator.subList() to any of its bulk operations (addAll, setAll, removeAll, retainAll) throws ConcurrentModificationException. The reason is that the VetoableListDecorator.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 for modCount 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

  • Change must not contain extraneous whitespace
  • Commit message must refer to an issue
  • Change must be properly reviewed (2 reviews required, with at least 1 Reviewer, 1 Author)

Issue

  • JDK-8347753: VetoableListDecorator doesn't accept its own sublists for bulk operations (Bug - P4)

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

@bridgekeeper
Copy link

bridgekeeper bot commented Jan 15, 2025

👋 Welcome back mstrauss! A progress list of the required criteria for merging this PR into master will be added to the body of your pull request. There are additional pull request commands available for use with this pull request.

@openjdk
Copy link

openjdk bot commented Jan 15, 2025

@mstr2 This change now passes all automated pre-integration checks.

ℹ️ This project also has non-automated pre-integration requirements. Please see the file CONTRIBUTING.md for details.

After integration, the commit message for the final commit will be:

8347753: VetoableListDecorator doesn't accept its own sublists for bulk operations

Reviewed-by: angorya, kcr

You can use pull request commands such as /summary, /contributor and /issue to adjust it as needed.

At the time when this comment was updated there had been 26 new commits pushed to the master branch:

  • f38ab33: 8349924: Additional WebKit 620.1 fixes from WebKitGTK 2.46.6
  • 0555fb2: 8349472: Update copyright header for files modified in 2025
  • 163bf6d: 8350437: [GHA] Update gradle wrapper-validation action to v3
  • 307e308: 8337960: Improve performance of mfwrapper by reusing GStreamer media buffers for decoded video
  • ae5864c: 8349758: Memory leak in TreeTableView
  • 065548d: 8349891: Not implemented function should have printf
  • d07d5cc: 8349105: Pagination: exception initializing in a background thread
  • 01059d4: 8349255: TitledPane: exception initializing in a background thread
  • d1f5ea8: 8349098: TabPane: exception initializing in a background thread
  • b267340: 8348095: [Linux] Menu shows up in wrong position when using i3 windows manager in full screen mode
  • ... and 16 more: https://git.openjdk.org/jfx/compare/a176574744978156edf8f83d8c0b2f994b36cd1c...master

As there are no conflicts, your changes will automatically be rebased on top of these commits when integrating. If you prefer to avoid this automatic rebasing, please check the documentation for the /integrate command for further details.

➡️ To integrate this PR with the above commit message to the master branch, type /integrate in a new comment.

@openjdk openjdk bot added the rfr Ready for review label Jan 15, 2025
@openjdk
Copy link

openjdk bot commented Jan 15, 2025

@mstr2
The total number of required reviews for this PR (including the jcheck configuration and the last /reviewers command) is now set to 2 (with at least 1 Reviewer, 1 Author).

@mlbridge
Copy link

mlbridge bot commented Jan 15, 2025

Webrevs


private class VetoableSubListDecorator implements List<E> {
/**
* Returns the specified collection as an unmodifiable list that can safely be used in all bulk
Copy link
Contributor

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?

Copy link
Collaborator Author

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.

Copy link
Contributor

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?

Copy link
Collaborator Author

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...

Copy link
Contributor

@andy-goryachev-oracle andy-goryachev-oracle left a 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});
}
Copy link
Contributor

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?)

Copy link
Collaborator Author

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.

@kevinrushforth kevinrushforth self-requested a review January 23, 2025 17:17
@mstr2
Copy link
Collaborator Author

mstr2 commented Jan 27, 2025

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.

I don't quite understand what you mean. Can you elaborate?
In particular, what does "save one extra pointer" mean?

@andy-goryachev-oracle
Copy link
Contributor

Can you elaborate?
what does "save one extra pointer" mean?

What I mean is simply get rid of the extra pointer like VetoableListIteratorDecorator.parent and the related logic, and simply create a copy of List/Collection each time.

@mstr2
Copy link
Collaborator Author

mstr2 commented Jan 27, 2025

Can you elaborate?
what does "save one extra pointer" mean?

What I mean is simply get rid of the extra pointer like VetoableListIteratorDecorator.parent and the related logic, and simply create a copy of List/Collection each time.

Okay, I see. We can't do that, because a sublist is specified to be a live view onto the source list. After all, list.subList(0, 3).clear() is valid code, and the result is that three elements are removed from the source list.

@andy-goryachev-oracle
Copy link
Contributor

We can't do that, because a sublist is specified to be a live view onto the source list.

good point, thanks for pointing this out.

Copy link
Contributor

@andy-goryachev-oracle andy-goryachev-oracle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@bridgekeeper
Copy link

bridgekeeper bot commented Feb 24, 2025

@mstr2 This pull request has been inactive for more than 4 weeks and will be automatically closed if another 4 weeks passes without any activity. To avoid this, simply add a new comment to the pull request. Feel free to ask for assistance if you need help with progressing this pull request towards integration!

Copy link
Member

@kevinrushforth kevinrushforth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code changes look good. I confirm that the new tests fail without the fix and pass with the fix.

@openjdk openjdk bot added the ready Ready to be integrated label Feb 25, 2025
@mstr2
Copy link
Collaborator Author

mstr2 commented Feb 25, 2025

/integrate

@openjdk
Copy link

openjdk bot commented Feb 25, 2025

Going to push as commit 4f0f992.
Since your change was applied there have been 26 commits pushed to the master branch:

  • f38ab33: 8349924: Additional WebKit 620.1 fixes from WebKitGTK 2.46.6
  • 0555fb2: 8349472: Update copyright header for files modified in 2025
  • 163bf6d: 8350437: [GHA] Update gradle wrapper-validation action to v3
  • 307e308: 8337960: Improve performance of mfwrapper by reusing GStreamer media buffers for decoded video
  • ae5864c: 8349758: Memory leak in TreeTableView
  • 065548d: 8349891: Not implemented function should have printf
  • d07d5cc: 8349105: Pagination: exception initializing in a background thread
  • 01059d4: 8349255: TitledPane: exception initializing in a background thread
  • d1f5ea8: 8349098: TabPane: exception initializing in a background thread
  • b267340: 8348095: [Linux] Menu shows up in wrong position when using i3 windows manager in full screen mode
  • ... and 16 more: https://git.openjdk.org/jfx/compare/a176574744978156edf8f83d8c0b2f994b36cd1c...master

Your commit was automatically rebased without conflicts.

@openjdk openjdk bot added the integrated Pull request has been integrated label Feb 25, 2025
@openjdk openjdk bot closed this Feb 25, 2025
@openjdk openjdk bot removed ready Ready to be integrated rfr Ready for review labels Feb 25, 2025
@openjdk
Copy link

openjdk bot commented Feb 25, 2025

@mstr2 Pushed as commit 4f0f992.

💡 You may see a message that your pull request was closed with unmerged commits. This can be safely ignored.

@mstr2 mstr2 deleted the fixes/vetoable-list-decorator-sublist branch July 26, 2025 20:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

integrated Pull request has been integrated

Development

Successfully merging this pull request may close these issues.

3 participants