JDK-8164629 : ListChangeListener changes event payload while executing the onChange method
  • Type: Bug
  • Component: javafx
  • Sub-Component: controls
  • Affected Version: 8u102
  • Priority: P4
  • Status: Closed
  • Resolution: Won't Fix
  • OS: other
  • CPU: x86
  • Submitted: 2016-08-19
  • Updated: 2017-06-26
  • Resolved: 2017-06-26
Related Reports
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.8.0_101"
Java(TM) SE Runtime Environment (build 1.8.0_101-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.101-b13, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Windows 10 Version 1511 Build 10586.545

A DESCRIPTION OF THE PROBLEM :
If you create a ListView instance and add a ListChangeListener to the selected items, it may happen that the list that is accessible via the Change object is modified while the onChanged method is executed. This happened to me for a Change which carried an addition to the list, as well as a removal. It seems to be happening consistently on the same machine. I could notice different behavior only if I switched computers.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
I added source code to reproduce the problem. If you execute it, you need to do the following:

1. Select the second item in the list
2. Hold down shift and click on the first item
3. Still holding shift click on the second item again

As this wasn't consistently happening to me on different computers, you may need to select the third item instead of the first one in step 2.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
An event is fired which does not alter it's state while being processed.
ACTUAL -
The list backing the Change instance changes. At first, it has a null reference. After the thread slept for around 300ms (on my machine, this may vary) there is one String instance in the list. At home, this happens for me on the removal change, at my work place it happened in the added change.

For me, the log file looks like this:

--- Next change (added/removed/permuted/replaced/updated) false true false false false
--- (change.getList().size()/added/removed) 1 0 1
= before sleep
item #0: null
= after sleep
item #0: item 2

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import javafx.application.Application;
import javafx.collections.ListChangeListener;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.scene.control.SelectionMode;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;

public class Demo extends Application {

    public static void main(String args[]) {
        launch(Demo.class);
    }

    public void start(Stage primaryStage) throws Exception {
        primaryStage.setTitle("Listchangelistener Problem");

        ListView<String> listView = new ListView<>();
        listView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
        listView.getSelectionModel().getSelectedItems().addListener(new ListChangeListener<String>() {
            public void onChanged(Change<? extends String> change) {
                while (change.next()) {
                    System.out.println(String.format("--- Next change (added/removed/permuted/replaced/updated) %b %b %b %b %b", change.wasAdded(), change.wasRemoved(), change.wasPermutated(), change.wasReplaced(), change.wasUpdated()));
                    System.out.println(String.format("--- (change.getList().size()/added/removed) %d %d %d", change.getList().size(), change.getAddedSize(), change.getRemovedSize()));

                    System.out.println("= before sleep");
                    printList(change);

                    try {
                        Thread.sleep(300);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    System.out.println("= after sleep");
                    printList(change);
                }
            }

            private void printList(Change<? extends String> change) {
                int count = 0;
                for (String entry : change.getList()) {
                    System.out.println("item #" + count++ + ": " + entry);
                }
            }
        });

        listView.getItems().add("test");
        listView.getItems().add("more test");
        listView.getItems().add("another test");

        primaryStage.setScene(new Scene(listView, 400, 600));
        primaryStage.show();
    }
}
---------- END SOURCE ----------


Comments
No plans to fix in JDK 8 at this time. If there's a customer or SQE concern then please file a new JBS issue.
26-06-2017

I doubt that either dev or sustaining will want to fix in 8u given that it is already fixed in 9. The exception would be if this were a regression introduced in an earlier 8u release. Do we know whether it is?
23-08-2016

For the sake of Jira cleanliness, I will mark this as cannot reproduce. If there is a desire for a backport / fix in JDK 8, we can continue to discuss that as necessary.
23-08-2016

I was able to reproduce this under some versions of JDK 8 (I haven't tested exhaustively), but I am unable to reproduce this issue under JDK 9 on Mac OS X or Windows 8. It isn't clear to me whether this issue should be closed as 'cannot reproduce', or whether there is some expectation that a backport should be considered? I'm leaning towards closing as cannot reproduce, but a +1 that this is the right direction would be appreciated.
23-08-2016

Since this is a similar issue to JDK-8144501, then is it also a regression (since the other was)?
23-08-2016

This is the case of JDK-8144501 for ListView.
23-08-2016