RFR: 8290310: ChangeListener events are incorrect or misleading when a nested change occurs [v6]

Nir Lisker nlisker at openjdk.org
Sat Feb 18 23:23:30 UTC 2023


On Wed, 15 Feb 2023 09:46:22 GMT, John Hendrikx <jhendrikx at openjdk.org> wrote:

>> This contains the following:
>> - Nested changes or invalidations using ExpressionHelper are delayed until the current emission completes
>>   - This fixes odd change events being produced (with incorrect oldValue)
>>   - Also fixes a bug in ExpressionHelper where a nested change would unlock the listener list early, which could cause a `ConcurrentModificationException` if a nested change was combined with a remove/add listener call
>> - A test for ExpressionHelper to verify the new behavior
>> - A test for all *Property and *Binding classes that verifies correct listener behavior at the API level (this tests gets 85% coverage on ExpressionHelper on its own, the only thing it is not testing is the locking behavior, which is not relevant at the API level).
>> - A fix for `WebColorFieldSkin` which triggered a nested change which used a flag to prevent an event loop (I've changed it now to match how `DoubleFieldSkin` and `IntegerFieldSkin` do it
>
> John Hendrikx has updated the pull request incrementally with one additional commit since the last revision:
> 
>   Fix no change detection
>   
>   Old text was uppercased while new text is always lowercase.

I tried this test:


     static void withRemovalAnd2Changes() {
        inv = 0;
        var property = new SimpleIntegerProperty(0);

        ChangeListener<? super Number> listenerA = (obs, ov, nv) -> {
            inv++;
            String spaces = spaces();
            System.out.println(spaces + " bA " + ov + "->" + nv + " (" + property.get() + ")");
            property.set(5);
            System.out.println(spaces + " aA " + ov + "->" + nv + " (" + property.get() + ")");
        };

        ChangeListener<? super Number> listenerB = (obs, ov, nv) -> {
            inv++;
            String spaces = spaces();
            System.out.println(spaces + " bB "  + ov + "->" + nv + " (" + property.get() + ")");
            property.removeListener(listenerA);
            property.set(6);
            System.out.println(spaces + " aB " + ov + "->" + nv + " (" + property.get() + ")");
        };

        property.addListener(listenerA);
        property.addListener(listenerB);

        property.set(1);
    }

which removes a listener in a nested event. With this patch I got the following:

1 bA 0->1 (1)
1 aA 0->1 (5)
  2 bB 0->1 (5)
    3 bB 5->6 (6)
    3 aB 5->6 (6)
  2 aB 0->1 (6)
      4 bA 1->6 (6)
        5 bB 6->5 (5)
          6 bB 5->6 (6)
          6 aB 5->6 (6)
        5 aB 6->5 (6)
      4 aA 1->6 (6)
            7 bB 1->6 (6)
            7 aB 1->6 (6)

Because listener A is removed in B, we hit the inconsistency with 1 listener again. I need to work through this, but I'm not convinced this is what the output should be. One glaring question is, are nested listener additions/removals also deferred, or do they take effect immediately, in which case they shouldn't receive deferred nested events I think.

-------------

PR: https://git.openjdk.org/jfx/pull/837


More information about the openjfx-dev mailing list