On Sun, 5 Feb 2023 20:11:35 GMT, John Hendrikx <jhendr...@openjdk.org> wrote:

> I don't think we should care about depth-first, breadth-first. The only thing 
> that I think is important here is that the contract of ChangeListener is 
> respected. I think that that contract should be:
...

I'll be more concrete. Here is my test program:


public class ListenersTest {

    private static int inv = 0;

    public static void main(String[] args) {
        with1Change();
    }

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

        ChangeListener<? super Number> listenerA = (obs, ov, nv) -> {
            inv++;
            String spaces = IntStream.range(1, inv).mapToObj(i -> "  
").reduce(String::concat).orElse("") + inv;
            System.out.println(spaces + " bA " + ov + "->" + nv + " (" + 
property.get() + ")");
            property.set(5);
            System.out.println(spaces + " aA " + ov + "->" + nv + " (" + 
property.get() + ")");
        };
        property.addListener(listenerA);

        ChangeListener<? super Number> listenerB = (obs, ov, nv) -> {
            inv++;
            String spaces = IntStream.range(1, inv).mapToObj(i -> "  
").reduce(String::concat).orElse("") + inv;
            System.out.println(spaces + " bB "  + ov + "->" + nv + " (" + 
property.get() + ")");
            System.out.println(spaces + " aB " + ov + "->" + nv + " (" + 
property.get() + ")");
        };
        property.addListener(listenerB);

        property.set(1);
        System.out.println("---------\n");
    }
}


With the patch:

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

With your patch, each event finishes its run and only then the next event 
happens. This is the "breadth-first" approach.
However, there is another one:

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

This approach starts events before the previous ones finished, and goes back to 
the original event later. This is the "depth-first" approach. I don't think 
that either is wrong. This one makes sense and it's a behavior I can reason 
about: the listener is loyal to the event at the time it happened (and the 
"real" value is accessible with `get`).

Without the patch:

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

I agree that at step 4 the 0->5 event is wrong because the events are only 0->1 
and 1->5.

If you comment out the line `property.addListener(listenerB);` (only register 
A), then both with and without the patch I get

1 bA 0->1 (1)
  2 bA 1->5 (5)
  2 aA 1->5 (5)
1 aA 0->1 (5)

while with delaying nested events I would expect:

1 bA 0->1 (1)
1 aA 0->1 (5)
  2 bA 1->5 (5)
  2 aA 1->5 (5)

So this looks inconsistent to me.

The fix for the lock being released is good regardless, it's the behavioral 
change that I'm not sold on.

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

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

Reply via email to