Dealing with nested events for a single property in JavaFX
Richard Bair
richard.bair at oracle.com
Thu Jun 21 17:36:11 PDT 2012
I don't recursion is the right phrase, but we fire the new event immediately. The problem with this is that two guys might be listening to the same event. The first guy is notified and changes the state of the thing, causing a new event to fire. The second guy gets this new event first, and reacts to it. The second guy then gets the first event and reacts to it. From his point of view, the second event happened first, and the first second. So we end up with a hopeless situation for the poor guy in a loosely coupled app. He doesn't know that he handled events out of ordered, and without violating encapsulation, he can't know.
Unless of course we have a timestamp on the event object and throw away events that occurred after the one he most recently handled.
Richard
On Jun 21, 2012, at 5:17 PM, steve.x.northover at oracle.com wrote:
> Exactly what is happening now? Are we recursing ? If so, although normally unwanted, I believe this is *correct*. It allows very smart people to make a change, ignore the event caused by it and keep going by stacking a boolean state within a listener. If we queue, then how do they do this?
>
> Steve
>
> On 21/06/2012 7:38 PM, Daniel Zwolenski wrote:
>> Jonathan's second JIRA (http://javafx-jira.kenai.com/browse/RT-17772) uses
>> a TextProperty with a ChangeListener, i.e. it is not limited to 'Event
>> events'.
>>
>>
>> On Fri, Jun 22, 2012 at 9:30 AM, Richard Bair<richard.bair at oracle.com>wrote:
>>
>>> No I believe the value would still be "true" immediately. And to be clear
>>> I'm talking about Event events here, not change notification listeners. So
>>> ActionEvent, MouseEvent, etc.
>>>
>>> But that does give pause to consider listeners as well. I don't think you
>>> can queue those up. So specifically I think this issue is about whether:
>>>
>>> node.fireEvent(newEvent)
>>>
>>> is layered or queued, without having to be placed on the actual EventQueue
>>> (ie: runLater).
>>>
>>> Richard
>>>
>>> On Jun 21, 2012, at 4:18 PM, Daniel Zwolenski wrote:
>>>
>>> It's not totally clear: I assume you are talking about a queue per
>>> property? Or do you mean one big queue for all properties?
>>>
>>> In either case I think we introduce a potential problem with this. Let's
>>> say we have:
>>>
>>> BooleanProperty one;
>>> BooleanProperty two;
>>> two.bind(one);
>>>
>>> Then in normal code we do this:
>>>
>>> one.set(true);
>>> two.get(); // value is 'true' as a result of binding
>>>
>>> But if we do the exact same code in a callback it has a different result:
>>>
>>> one.onValueChanged() {
>>> one.set(true);
>>> two.get(); // value is 'false' as event hasn't been processed yet
>>> }
>>>
>>> With a queue per property the problem is limited to only when you are
>>> listening on your own value and just maybe we could get away with that (I'm
>>> not convinced though, 'feels' dangerous). With one big queue it is really
>>> nasty as it would totally change the semantics of code depending on whether
>>> it was being called from within a property callback or not.
>>>
>>>
>>>
>>> On Fri, Jun 22, 2012 at 8:54 AM, Richard Bair<richard.bair at oracle.com>wrote:
>>>
>>>> Right, so what it comes down to is: do we layer events, or queue events?
>>>> RunLater is wrong because we might insert a pulse between events on the
>>>> queue so you will render things incorrectly for some brief period of time.
>>>>
>>>> It seems like handling events sequentially as they occur is the right
>>>> semantic, rather than the "onion" approach where partway through handling
>>>> event 1 we issue event 2. The problem here is that event 2 is handled, and
>>>> then the rest of event 1 is handled, which now has the wrong state (if it
>>>> was carrying state related to that first event.
>>>>
>>>> Of course by queuing events it is possible people will end up with
>>>> handling so many events that the app thread is always busy and not
>>>> processing the queue -- but then they can get into infinite recursion and
>>>> all kinds of other issues, so I don't see that as a problem but just the
>>>> nature of the beast.
>>>>
>>>> Richard
>>>>
>>>> On Jun 21, 2012, at 3:47 PM, Jonathan Giles wrote:
>>>>
>>>>> The point is: can we have a better solution by considering changing the
>>>> event API such that it queues events, rather than do them immediately as
>>>> they are fired.
>>>>> -- Jonathan
>>>>>
>>>>> On 22/06/2012 10:46 a.m., Slavko Scekic wrote:
>>>>>> So, the point is: be careful? :)
>>>>>>
>>>>>> On Fri, Jun 22, 2012 at 12:39 AM, Jonathan Giles<
>>>> jonathan.giles at oracle.com<mailto:jonathan.giles at oracle.com>> wrote:
>>>>>> runLater is wrong more often than it is right - so you're right -
>>>>>> I don't think we should consider that approach (or requiring
>>>>>> people to have to know to do this)
>>>>>>
>>>>>> Saying that you should error out when someone tries to update a
>>>>>> property in the event callback is also wrong, I think. There are
>>>>>> legitimate use cases, for example as a last ditch form of
>>>>>> validation: if the value is outside the allowable range, change it
>>>>>> back to something that is valid. Alternatively, when focus is
>>>>>> given to a node, request a focus change on another node. I imagine
>>>>>> our own code would blow up if we took this approach :-)
>>>>>>
>>>>>> -- Jonathan
>>>>>>
>>>>>>
>>>>>> On 22/06/2012 10:31 a.m., Daniel Zwolenski wrote:
>>>>>>
>>>>>> Similar to the ConcurrentModification thing you get with
>>>>>> Collections right? Could it be handled in a similar way, i.e.
>>>>>> throw an error if someone tries to update the property they
>>>>>> are modifying while in the update callback for that property?
>>>>>> As you say, it's user error, so slapping them on the wrists is
>>>> ok.
>>>>>> The runLater one feels like it could cause its own problems to
>>>>>> me. The 'single' threadedness of JFX is part of it's design.
>>>>>> It gives me deterministic behaviour, this feels like it could
>>>>>> open up small cracks in that. Obviously we wouldn't get
>>>>>> concurrency/deadlock issues but I suspect we could get things
>>>>>> in a non-deterministic order as a result of this (e.g. if
>>>>>> another thread does a runLater somewhere else in the code at
>>>>>> the same time this runLater is being added). Could end up that
>>>>>> my property change is overwritten sometimes but not others,
>>>>>> etc (I'm going more off gut feel here though than concrete
>>>>>> examples).
>>>>>>
>>>>>>
>>>>>> On Fri, Jun 22, 2012 at 8:20 AM, Jonathan Giles
>>>>>> <jonathan.giles at oracle.com<mailto:jonathan.giles at oracle.com>
>>>>>> <mailto:jonathan.giles at oracle.com
>>>>>> <mailto:jonathan.giles at oracle.com>>> wrote:
>>>>>>
>>>>>> Hi all,
>>>>>>
>>>>>> I'm going to keep this brief as I'm fairly comprehensively
>>>>>> underwater on the bug count.
>>>>>>
>>>>>> Recently I've found a pattern of bug that, well, I'm fairly
>>>>>> sure
>>>>>> is due to user error, but is not obvious at all (and as such
>>>> it
>>>>>> leads to bug reports). In the last week, I've encountered
>>>> this
>>>>>> issue twice. The basic issue is that of listening to an
>>>>>> event (for
>>>>>> example, a focus change event), and reacting in such a way
>>>>>> as to
>>>>>> modify the state of this property (which results in another
>>>>>> event
>>>>>> being fired). The end result is non-deterministic (but often
>>>>>> broken behavior). Interestingly, it has in both my cases
>>>>>> manifested itself as something that works once, and then
>>>> fails
>>>>>> after that forever more.
>>>>>>
>>>>>> In both cases, after much code digging and debugging
>>>> (although
>>>>>> today was made much easier by the same issue last week), I
>>>>>> believe
>>>>>> the issue can be worked around simply by wrapping the change
>>>> to
>>>>>> the property state (in the event callback) with a
>>>>>> Platform.runLater(new Runnable() { ...}). This forces the
>>>>>> second
>>>>>> property update to happen after the first event has finished
>>>>>> firing (at some point in the future).
>>>>>>
>>>>>> However, this isn't a great solution - we're forcing the
>>>>>> event to
>>>>>> fire at a later date where the state may have already
>>>>>> changed. The
>>>>>> better solution, in my opinion, is to improve the event
>>>> system
>>>>>> such that it knows whether an event is already firing, and
>>>>>> if so
>>>>>> it will queue up the event to run after the current one has
>>>>>> finished. I would be interested in hearing whether anyone
>>>>>> else has
>>>>>> encountered this kind of bug, or whether they have better
>>>>>> suggestions.
>>>>>>
>>>>>> You can see two examples of this bug in the code attached
>>>> here
>>>>>> (where the first example is for ComboBox where the value is
>>>>>> updated in the onAction callback....which is called when
>>>> value
>>>>>> changes):
>>>>>>
>>>>>> http://javafx-jira.kenai.com/browse/RT-22478
>>>>>> http://javafx-jira.kenai.com/browse/RT-17772
>>>>>>
>>>>>> -- Jonathan
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>
>>>>
>>>
More information about the openjfx-dev
mailing list