Re: JavaFX Application Thread is recursively re-entrant into Eventhandler handle() method under some circumstances
I am reading your stack trace but I defintely never mentioned the double invocation to handle that I see there are evidencing anything. The issue is the value of debugCounter at a two certain moments in the application - in the two calls to showDebugInformation. Although the proof that I am right is contained in the value debugCounter in the method showDebugInformation in the current program, if you go to the link in my previous post and download and run the Complex version of this, you can more easily see the departure of the JavaApplicationThread from the instance of handle which is on the stack - and at a method which is not handle (as it is in this one) followed by it's recursive re-entry to handle , completion of that recursive-handle's execution and re-entry and then completion into the previous invocation of handle. It's is completely obvious in the output of that Applcation. That application also throws the ConcurrentModificationException which is no way an artifact or misreading of a stacktrace. However, what is at issue in this program is the value of debugCounter in showDebugInformation. Cheers. On Sunday, September 9, 2018 at 4:04 PM, John Hendrikx wrote: I see nothing special in the stack trace. When you remove the component, a new MouseEvent *must* trigger (MouseEvent.EXITED) as it always needs to match with MouseEvent.ENTERED. So, the call to 'remove' triggers a new event, which gets handled by the same handler. It is indeed entered recursively, but in a normal fashion. This has nothing to do with another thread or compiler bugs. Don't be confused by the double "handle" lines in the stacktrace. This happens when methods are overriden (the line number is 1). There are two relevant lines in this trace: LabelEventHandler.handle(LabelEventHandler.java:95) ...where Remove is called, which triggers the recursive call later on: LabelEventHandler.handle(LabelEventHandler.java:88) ... for the MouseEvent.EXITED event. The full stack trace is this: at bareBonesJavaFXBugExample.LabelEventHandler.handle(LabelEventHandler.java:88) at bareBonesJavaFXBugExample.LabelEventHandler.handle(LabelEventHandler.java:1) at javafx.base/com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218) at javafx.base/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80) at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238) at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191) at javafx.base/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59) at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58) at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56) at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) at javafx.base/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74) at javafx.base/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49) at javafx.base/javafx.event.Event.fireEvent(Event.java:198) at javafx.base/com.sun.javafx.event.EventQueue.fire(EventQueue.java:48) at javafx.graphics/javafx.scene.Scene$MouseHandler.handleNodeRemoval(Scene.java:3717) at javafx.graphics/javafx.scene.Scene$MouseHandler.access$7800(Scene.java:3604) at javafx.graphics/javafx.scene.Scene.generateMouseExited(Scene.java:3601) at javafx.graphics/javafx.scene.Parent$3.onProposedChange(Parent.java:613) at javafx.base/com.sun.javafx.collections.VetoableListDecorator.remove(VetoableListDecorator.java:329) at javafx.base/com.sun.javafx.collections.VetoableListDecorator.remove(VetoableListDecorator.java:221) at bareBonesJavaFXBugExample.LabelEventHandler.handle(LabelEventHandler.java:95) at bareBonesJavaFXBugExample.LabelEventHandler.handle(LabelEventHandler.java:1) at javafx.base/com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218) (... rest cut off as it is not needed ... ) --John On 09/09/2018 19:05, jav...@use.startmail.com wrote: Hi All, I spent some time refactoring the program which displays this bug. It's now small enough to share the source in an email, but it is very very dense and the proof of bug, one specific line to standard I/O, requires the source code to be read and understood in order to see the bug. As I said previously, more comprehensible and user-friendly versions of this program are available at . The javadoc is more copious, the bug is manifested as an exception and the side effect of the bug are more consequential. This brief version cnosists of just two classes. H
Re: JavaFX Application Thread is recursively re-entrant into Eventhandler handle() method under some circumstances
I did not say it was another Thread. In fact, I said it was the Java Application Thread itself- being recursively re-entrant. Specifically, at the time the MOUSE_EXITED_TARGET event is generated, the Java Application Thread has a choice- continue proocessing the EventHandler#handle method which it is in, or respond to the MOUSE_EXITED_TARGET event which was just generated. Suprisingly, it does the second , completes that call to handle, then the stack pops and it nfinishes the previous invocation of handle. It's not about another Thread. I don't refer to the stack trace as proof that the thread is reentrant. I do say the contrapositve of that, which is, if the thread trace didn't show the bug then the bug wouldn't exist. But I don't assert the stack trace as a proof. The proof is the proof I gave in the javadoc, and that is the value of one the static int debugCounter acheives in the debug statements can only be explained if recursive re-entrancy has cocurred, which is true. You need to address this. Also, this is merely the most barebones implementation which reveals this bug. This is why I didn't want to post it, because you have to work out what cannot be the case before evaluating the argument. What cannot be the case is debugCounter cannot have value 1 in the debug output method. The link provides a more consequential / more easily understood bug / less dismissable bug- a ConcurrentModificationException against a Set which the EventHandler is iterating through. That CME happens not because two threads are operating on the Set but because one thread , the JAva Application Thread is modifying the Set while it is also iterating through it. On Sunday, September 9, 2018 at 4:04 PM, John Hendrikx wrote: I see nothing special in the stack trace. When you remove the component, a new MouseEvent *must* trigger (MouseEvent.EXITED) as it always needs to match with MouseEvent.ENTERED. So, the call to 'remove' triggers a new event, which gets handled by the same handler. It is indeed entered recursively, but in a normal fashion. This has nothing to do with another thread or compiler bugs. Don't be confused by the double "handle" lines in the stacktrace. This happens when methods are overriden (the line number is 1). There are two relevant lines in this trace: LabelEventHandler.handle(LabelEventHandler.java:95) ...where Remove is called, which triggers the recursive call later on: LabelEventHandler.handle(LabelEventHandler.java:88) ... for the MouseEvent.EXITED event. The full stack trace is this: at bareBonesJavaFXBugExample.LabelEventHandler.handle(LabelEventHandler.java:88) at bareBonesJavaFXBugExample.LabelEventHandler.handle(LabelEventHandler.java:1) at javafx.base/com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218) at javafx.base/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80) at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238) at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191) at javafx.base/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59) at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58) at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56) at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) at javafx.base/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74) at javafx.base/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49) at javafx.base/javafx.event.Event.fireEvent(Event.java:198) at javafx.base/com.sun.javafx.event.EventQueue.fire(EventQueue.java:48) at javafx.graphics/javafx.scene.Scene$MouseHandler.handleNodeRemoval(Scene.java:3717) at javafx.graphics/javafx.scene.Scene$MouseHandler.access$7800(Scene.java:3604) at javafx.graphics/javafx.scene.Scene.generateMouseExited(Scene.java:3601) at javafx.graphics/javafx.scene.Parent$3.onProposedChange(Parent.java:613) at javafx.base/com.sun.javafx.collections.VetoableListDecorator.remove(VetoableListDecorator.java:329) at javafx.base/com.sun.javafx.collections.VetoableListDecorator.remove(VetoableListDecorator.java:221) at bareBonesJavaFXBugExample.LabelEventHandler.handle(LabelEventHandler.java:95) at bareBonesJavaFXBugExample.LabelEventHandler.handle(LabelEventHandler.java:1) at javafx.base/com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218) (... rest cut off as it is not needed ... ) --John On 09/09/2018 19:05, jav...@use.startmail.com wrote:
Re: JavaFX Application Thread is recursively re-entrant into Eventhandler handle() method under some circumstances
I see nothing special in the stack trace. When you remove the component, a new MouseEvent *must* trigger (MouseEvent.EXITED) as it always needs to match with MouseEvent.ENTERED. So, the call to 'remove' triggers a new event, which gets handled by the same handler. It is indeed entered recursively, but in a normal fashion. This has nothing to do with another thread or compiler bugs. Don't be confused by the double "handle" lines in the stacktrace. This happens when methods are overriden (the line number is 1). There are two relevant lines in this trace: LabelEventHandler.handle(LabelEventHandler.java:95) ...where Remove is called, which triggers the recursive call later on: LabelEventHandler.handle(LabelEventHandler.java:88) ... for the MouseEvent.EXITED event. The full stack trace is this: at bareBonesJavaFXBugExample.LabelEventHandler.handle(LabelEventHandler.java:88) at bareBonesJavaFXBugExample.LabelEventHandler.handle(LabelEventHandler.java:1) at javafx.base/com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218) at javafx.base/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80) at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238) at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191) at javafx.base/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59) at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58) at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56) at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) at javafx.base/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74) at javafx.base/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49) at javafx.base/javafx.event.Event.fireEvent(Event.java:198) at javafx.base/com.sun.javafx.event.EventQueue.fire(EventQueue.java:48) at javafx.graphics/javafx.scene.Scene$MouseHandler.handleNodeRemoval(Scene.java:3717) at javafx.graphics/javafx.scene.Scene$MouseHandler.access$7800(Scene.java:3604) at javafx.graphics/javafx.scene.Scene.generateMouseExited(Scene.java:3601) at javafx.graphics/javafx.scene.Parent$3.onProposedChange(Parent.java:613) at javafx.base/com.sun.javafx.collections.VetoableListDecorator.remove(VetoableListDecorator.java:329) at javafx.base/com.sun.javafx.collections.VetoableListDecorator.remove(VetoableListDecorator.java:221) at bareBonesJavaFXBugExample.LabelEventHandler.handle(LabelEventHandler.java:95) at bareBonesJavaFXBugExample.LabelEventHandler.handle(LabelEventHandler.java:1) at javafx.base/com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218) (... rest cut off as it is not needed ... ) --John On 09/09/2018 19:05, jav...@use.startmail.com wrote: Hi All, I spent some time refactoring the program which displays this bug. It's now small enough to share the source in an email, but it is very very dense and the proof of bug, one specific line to standard I/O, requires the source code to be read and understood in order to see the bug. As I said previously, more comprehensible and user-friendly versions of this program are available at . The javadoc is more copious, the bug is manifested as an exception and the side effect of the bug are more consequential. This brief version cnosists of just two classes. Here is the JavaFX Application class: *** package bareBonesJavaFXBugExample; import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.control.Label; import javafx.scene.input.MouseEvent; import javafx.scene.layout.Pane; import javafx.stage.Stage; /** * An {@link Application} with one {@link Pane} containing one {@link Label}. The {@link Label} has a single {@link javafx.event.EventHandler}, {@link LabelEventHandler} which processes all {@link MouseEvent}s the {@link Label} receives. * * To trigger the bug, run the application, then spend a second mouse over the little label in the upper left hand corner of the scrren. You will see output to standard I/O. Then, click the label, which will then disppear. Check the I/O for Strings ending in debugCounter is 1. * What that String means and how it proves that the JavaFX Application Thread has become reentrant is explained in the javadoc of {@link LabelEventHandler}. */ public class JavaFXAnomalyBareBonesApplication extends Application { public void start(Stage primaryStage)
Re: JavaFX Application Thread is recursively re-entrant into Eventhandler handle() method under some circumstances
Hi All, I spent some time refactoring the program which displays this bug. It's now small enough to share the source in an email, but it is very very dense and the proof of bug, one specific line to standard I/O, requires the source code to be read and understood in order to see the bug. As I said previously, more comprehensible and user-friendly versions of this program are available at . The javadoc is more copious, the bug is manifested as an exception and the side effect of the bug are more consequential. This brief version cnosists of just two classes. Here is the JavaFX Application class: *** package bareBonesJavaFXBugExample; import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.control.Label; import javafx.scene.input.MouseEvent; import javafx.scene.layout.Pane; import javafx.stage.Stage; /** * An {@link Application} with one {@link Pane} containing one {@link Label}. The {@link Label} has a single {@link javafx.event.EventHandler}, {@link LabelEventHandler} which processes all {@link MouseEvent}s the {@link Label} receives. * * To trigger the bug, run the application, then spend a second mouse over the little label in the upper left hand corner of the scrren. You will see output to standard I/O. Then, click the label, which will then disppear. Check the I/O for Strings ending in debugCounter is 1. * What that String means and how it proves that the JavaFX Application Thread has become reentrant is explained in the javadoc of {@link LabelEventHandler}. */ public class JavaFXAnomalyBareBonesApplication extends Application { public void start(Stage primaryStage) { Pane mainPane = new Pane(); mainPane.setMinHeight(800); mainPane.setMinWidth(800); Label label = new Label(" this is quite a bug "); LabelEventHandler labelEventHandler = new LabelEventHandler(mainPane, label); label.addEventHandler(MouseEvent.ANY, labelEventHandler); mainPane.getChildren().add(label); Scene scene = new Scene(mainPane); primaryStage.setScene(scene); primaryStage.show(); } /** * The entry point of application. * * @param args * the input arguments */ public static void main(String[] args) { launch(args); } } *** and here is its only dependency, the EventListener class. I included enough javadoc to have the program makes sense. : *** package bareBonesJavaFXBugExample; import javafx.event.Event; import javafx.event.EventHandler; import javafx.scene.control.Label; import javafx.scene.input.MouseEvent; import javafx.scene.layout.Pane; import java.util.Collection; import java.util.ConcurrentModificationException; /** * An {@link EventHandler} implementation for {@link MouseEvent}s. This implementation's {@link EventHandler#handle(Event)} shows the relevant debug information to standard output before and after removing the member {@link #label} from the {@link #pane}. * * discussion * * Users should first satisfy themselves that the value of {@link LabelEventHandler#debugCounter} can only be non-zero, in fact 1 (one) in the method {@link LabelEventHandler#showDebugInformation(String)} if the method {@link LabelEventHandler#handle(MouseEvent)} has been re-entered recursively, that is, before a previous invocation of {@link LabelEventHandler#handle(MouseEvent)} has returned. * * Proof: 1) debugCounter starts at value 0 (zero). 2) debugCounter is only incremented once, by 1 (one), and that is after the first call to {@link LabelEventHandler#showDebugInformation(String)} has returned. 3) debugCounter is only decremented once, by 1 (one) and that is before the last call to {@link LabelEventHandler#showDebugInformation(String)}. 4) however, because * debugCounter is a class variable ( it's static), if handle() is recurvsively re-entered then it's value can be 1 (one) when the re-entrant Thread executes {@link LabelEventHandler#showDebugInformation(String)} * * End proof. * * * * The output of this method to standard I/O is volumnious but searching the output for the exact String "debugCounter is 1" will immediately show the {@link LabelEventHandler#handle(MouseEvent)} method to have been recursively entered. * Some other possibilities other than the JavaFX Application Thread recursing into {@code handle()} need to be addressed. One is the fact that the compiler is free to reorder statements if it can * prove that such a reordering would have no effect on the program's correctness. * * So somehow the compiler is reordering the increment/decrement of {@code debugCounter} and the calls to {@code showDebugInformation}. But this would al