On Wed, 21 Oct 2026 11:47:14 GMT, Marius Hanl <[email protected]> wrote:

>>> > I do have a solution for this, that I use for my own programs. They're 
>>> > basically a different kind of property that allows the owner of the 
>>> > property (ie. the control, table, whatever) to control when listeners 
>>> > fire (something we see already in some areas of FX where properties are 
>>> > "customized" to delay firing, but that's usually manual and not 
>>> > coordinated across multiple properties).
>>> 
>>> I'm all ears. A solution to this problem in JavaFX would be great. Yeah 
>>> JavaFX already uses this mechanism for e.g. `focusProperty` and it makes 
>>> sense.
>>> 
>>> > For example, if I need to change 3 properties, but they're all related 
>>> > (let's say x,y,z) I can change all three, and only at the end of my 
>>> > changes have the listeners fire.
>>> 
>>> This is exactly what we need.
>> 
>> In my FX Flow library, I have a solution for externally read only properties 
>> `UpdatableValue`, but general solutions can be built as well. It works like 
>> this:
>> 
>>     // these are private inside some class (like a ReadOnlyPropertyWrapper)
>>     private final UpdatableValue<Double> x = UpdatableValue.of();
>>     private final UpdatableValue<Double> y = UpdatableValue.of();
>> 
>>     // UpdatableValue don't have a normal "set" or "setValue" method at all,
>>     // instead manipulate using `UpdatableValue.set` method:
>>     UpdatableValue.set(
>>         x, 100,  // these are all typed parameters, despite how it looks
>>         y, 100
>>     );
>> 
>>     // or using `batch` method:
>>     UpdatableValue.batch(() -> {
>>       UpdatableValue.set(x, 100);
>>       UpdatableValue.set(y, 100);
>>       // supports nesting, can start another batch here, listeners only
>>       // fire at the end of the top most level batch
>>     });
>> 
>>     // exposing as read only:
>>     public final ObservableValue<Double> x() {
>>         return x.asObservableValue();
>>     }
>> 
>> For more arbitrary property manipulation, I've also got a different system, 
>> but it requires wrapping all properties that should participate in a 
>> transaction. If we ever add something like this to FX (where we can change 
>> the property code itself) the wrapping may not be needed. The 
>> `UpdatableValue` system is more light-weight, and works much more like how 
>> read only properties work.
>
>> For more arbitrary property manipulation, I've also got a different system, 
>> but it requires wrapping all properties that should participate in a 
>> transaction. If we ever add something like this to FX (where we can change 
>> the property code itself) the wrapping may not be needed. The 
>> `UpdatableValue` system is more light-weight, and works much more like how 
>> read only properties work.
> 
> This looks promising.
> 
> I also thought about a potential system and what I came up with (untested, 
> just a random shower thought really):
> 
> var listenerCallback = property.setSilent(12);
> ...
> listenerCallback.run();
> 
> 
> Then, the same idea like your batch, adding all those callbacks to a `List` 
> and then run them sequentially.
> This was based on how I would like to see the CSS code, because there is so 
> much logic in for loops, that this was the only idea where we could first 
> 'collect' callbacks and then later execute them sequentially.
> 
> It is really the same concept as your idea, but with more overhead and the 
> CSS stuff in mind.
> And your idea works like a single transaction, which I like more. 
> 
> Under the hood, do you still notify each listener sequentially, or do you 
> have another logic in place for such a transaction?
> If we find a good way how we could use this with some kind of 'collect' phase 
> / easy mechanism to track and then fire the transaction, I think this might 
> solve all problems.
> 
> Also does this solve the issues that were reported (also partly by you) where 
> sometimes JavaFX methods are called in a re-entrant way, messing up the 
> layout or internal state completely?
> 
> I think cases like this: https://bugs.openjdk.org/browse/JDK-8387639 and 
> https://bugs.openjdk.org/browse/JDK-8369617 are the usual result of this.

The code is here: 
https://github.com/int4-org/FX/blob/master/fx-core/src/main/java/org/int4/fx/core/util/UpdatableValue.java

To answer your question, it uses a custom observable value internally that can 
be asked to fire events manually and will never fire it automatically. All 
changes are gathered in a thread local `Set`, and once a batch ends, it just 
triggers the listeners on all of them sequentially indeed. Because it is a 
`Set`, it won't send a notification twice for the same property.

If you listen on both `x` and `y` you will get notified twice still, but you 
can't observe a wrong x/y pair. I also have something to avoid that on the 
listener side of things:

    Observe.values(x, y).subscribe((x, y) -> { ... });

That one will observe both properties, and call the subscriber when either `x` 
or `y` changes and won't call it again if an invalidation listener on `x` or 
`y` was called but there was no change. Combining both solutions you get atomic 
manipulation of x/y on the writer side, and you get a single callback on the 
reader side.

> Also does this solve the issues that were reported (also partly by you) where 
> sometimes JavaFX methods are called in a re-entrant way, messing up the 
> layout or internal state completely?

Well, I can't speak for all situations, and the layout system involves way more 
than just a handful of properties in a single class, but generally **if** you 
can delay when listeners fire, then there is no re-entrancy problem because you 
can trigger the listeners after your code is done (it will still nest on the 
stack, but you can control exactly where that happens).

Before #1081 I explored doing breadth first notification. This requires you to 
delay nested changes (so you can't get re-entrancy, but requires recording 
changes). The major problem with that approach was that the listener callbacks 
would go out of sync with what `getValue` says -- if I delay a nested listener 
notification, and just continue with the next one at the same level, that 
listener would observe a `getValue` that isn't the same as its `newValue`. It 
could work, but it's too late for FX to do this as it requires every listener 
to realize that only `newValue` should be acted upon.  There is just too much 
reliance on depth first (nested) notifications.

> I think cases like this: https://bugs.openjdk.org/browse/JDK-8387639 and 
> https://bugs.openjdk.org/browse/JDK-8369617 are the usual result of this.

Unless there's a good reproducer attached, a lot of these are likely the result 
of doing something from a thread other than the FX thread. Re-entrancy problems 
are deterministic, so there should be a reproducer.

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

PR Review Comment: https://git.openjdk.org/jfx/pull/2347#discussion_r4336578487

Reply via email to