On Fri, 17 Apr 2026 10:12:22 GMT, Marius Hanl <[email protected]> wrote:

>> Hi,
>> 
>>  From what I can see this type of behavior is inherent for an implementation 
>> in which when event is "fired" it is delivered right in place to listeners 
>> like in below pseudo code:
>>  
>> fireXXX()...
>> {
>>   for each listener invoke onXXX(....)
>> }
>> 
>> It is common for all awt and swing listeners and may, in plenty of 
>> conditions when nested firing occurs, lead to change in ordering of events. 
>> Yet, as far as I investigated it, and I did put a lot of work into it more 
>> than decade ago, it is a nature of in-place events firing. 
>> 
>> As I do see it, the very crucial assumption behind in-place firing is: 
>> _"when fireXXX returns, all listeners are notified"_. This is a strong 
>> indication of state and data consistency and only in-place firing may do it. 
>> Like in this pseudo code:
>> 
>> my_function()
>> {
>>   fireXXX() 
>>    _here I can assume that all listeners reacted to XXX_
>>   fireYYY()
>>    _here I can assume that all listeners reacted to XXX and YYY_
>> }
>> 
>> I can assume it regardless if `my_function()` was invoked in response to 
>> event or not.
>> 
>> This assumption is in a strict and conceptual conflict with _"when fireXXX 
>> is invoked the events reach all listeners in the same order as in which 
>> events were fired even if events are fired from inside of listener event 
>> handler"_.
>> 
>> Because now if my example function is invoked inside a listener those 
>> assumptions may be false.
>> 
>> Any attempt to achieve the goal of delivering events in their order of 
>> appearance will break the first assumption and it will lead to corruption of 
>> existing programs. In many, many funny ways.
>> 
>> Also any reliance on _"if listener A was registered before listener B, then 
>> A will get called before B"_ is a horribly bad idea. Please, do not even try 
>> to suggest it. Simply do not. There is no way to ensure that ordering in any 
>> system of some major complexity.
>> 
>> Any removing of "not necessary changes" in notification process is begging 
>> for troubles. It did change, and it did changed again. Preventing that first 
>> change from reaching the listeners is horrible idea, as listener is by 
>> definition getting every change, not just "some". 
>> 
>> In my practice, if I am worried about nested events and ordering problem, I 
>> do consciously decide to "decouple" them. Instead of firing an event in 
>> place I do, consciously, wrap them and delegate to the event pump (in case 
>> of awt - with invokeLater). Then I do know that they will reach the 
>> destination in order of firing, but I also do now that at...
>
>> Also any reliance on _"if listener A was registered before listener B, then 
>> A will get called before B"_ is a horribly bad idea. Please, do not even try 
>> to suggest it. Simply do not. There is no way to ensure that ordering in any 
>> system of some major complexity.
> 
> This is the case right now and will be after this PR.
> 
>> My recommendation is: if You really plan to change in-place firing to some 
>> more sophisticated algorithm **do not do it**. Not in a stable production 
>> code. Recommend, inform, educate about possible side effect, alternatively 
>> add like "orderedFireXXX" methods or something, but do not change such an 
>> essential algorithm. As you can see above delegation is a simple method 
>> which allows programmers using the library to decide what is important from 
>> them and what is not. If they see problems with ordering delegation solves 
>> it. If however you will build it in, they will have no way to ensure that if 
>> they have problems with:
> 
> I still don't get your point. This is about nested changes and that the old 
> value is misleading or simply incorrect. You talked about state consistency, 
> but currently, this can happen:
> 
> 
> myProperty.addListener((_, oldValue, newValue) -> {
>     // Here, oldValue MIGHT not be the last set value, but there value before 
> that.
>     // Instead of A -> B -> C
>     // oldValue can be A and newValue be C. Which is very bad when you want 
> to remove/unregister the old value.
> }
> 
> This PR corrects that to BE consistent, so the `oldValue` will be B in this 
> example. This is consistency for me.

@Maran23 

My point was, that events broadcasting strategy is a critical point of the 
system which is build around event oriented programming and model-observer 
design. A system which exists for about thirty years now and is deployed in 
millions of places. Touching it should be done with absolute care and attention.

I wished to make an impression, that what one person sees as an obvious benefit 
may be a complete drama for others. And this is obviously a mission critical 
point. It should not be touched to make it better. An alternative method shall 
be provided, the old might become deprecated, but such a critical subsystem 
shall not change its behavior.

My second observation was, that if one really needs proper ordering of events, 
one may resort to `invokeLater` or override some methods.

Consistency depends on point of view.

For an example, can you give a warranty that in any nested condition when event 
is fired, and the firing method returns, regardless if it was a nested firing 
or not, that specific event was delivered to listeners? If not, it will be a 
critical change, as a delivery time consistency becomes broken.

Did you consider how will it behave when one changed multiple properties on 
multiple objects, of which some broadcast will be nested and some will be not?

Then, some applications might rely on counting the changes, logging them or 
something like that. Will they still see everything?

Did you consider the consistency of the event with a value retrieved from the 
getters?

Sure, the in-place event firing has a lot of down-sides. The getters may 
produce value inconsistent with what event sends, the events order may flip, a 
deadlock possibilities are horrible is one will use `synchronized` inside a 
listener. A complete horror. But it is simple, well defined and... and all 
those downsides are built in the concept. It is enough to read it and 
understand to see them. If one needs a different concept, like for an example I 
when I work with multi-threaded systems where there is a lot of listeners, then 
one can figure out the event broadcasting strategy which will fit their needs, 
and plug it into they objects they like.

I do appreciate, that you did notice the problem, because very few people can 
see it. This is great. But I would rather see a safer approach with, for an 
example, pluggable listener manager or something like that, when I could put it 
in if I want it, or stay with an old style if I do not. This would be nice, 
because I could then plug in my own solution to my own problems. Yet, for those 
I can override some methods, right?

Where I see the problem, it is that the concept of having both getter and an 
event sending the new value is inherently flawed. Either getter and "changed" 
without to what, or no getter and only "streaming of changes" - only those can 
ensure the consistency.

Best regards,

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

PR Comment: https://git.openjdk.org/jfx/pull/1081#issuecomment-4287101343

Reply via email to