JDK-8102211 : Change FX startup such that the Application is initialized and constructed in the FX application thread
  • Type: Enhancement
  • Component: javafx
  • Sub-Component: application-lifecycle
  • Affected Version: 8
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2013-03-01
  • Updated: 2015-06-16
  • Resolved: 2013-10-18
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 8
8Fixed
Related Reports
Duplicate :  
Relates :  
Relates :  
Relates :  
Description
Currently, an FX application is loaded and constructed on a background thread rather than the FX application thread. The init() method is also called on a background thread.

We may want to consider changing the specification and implementation of the JavaFX launcher, for both standalone applications and for JNLP applications / applets, so that the Application is loaded and constructed in the FX application thread. The init() method should still be called on a background thread. We would need to do this is such a way that performance is not impacted. In particular we need to ensure that an application can still be initialized in parallel via the init() method while a preloader is running.

See the discussion in RT-28574 for more background information.

Quoting Richard from that JIRA:

"the platform will be easier to understand and the behavior more predictable if the developer is not exposed to multiple threads unless they take action to do so. Simplifying life for developers is A Good Thing (tm), and reducing bugs they might encounter even better. Not to mention the bugs we will encounter in our own platform!

Simple example, creating a Scene in the constructor or as a field on the class. A major no-no today (even though there is no reason we shouldn't allow scene creation on a background thread. Heck, you should be able to create a Stage on a background thread, as long as you don't try to show it)."
Comments
NOTE: I filed RT-36581 to track an IntelliJ bug that came up during the discussion of this issue.
08-04-2014

Yes, this is definitely a new issue. And as Steve said, if you aren't using the standard Java launcher to launch a subclass of javafx.application.Application then you need to ensure that JavaFX is initialized before you access any other JavaFX class. This can be done in one of the following two ways: 1) Call Application.launch(AppClass.class) where AppClass is a subclass of Application -- this is the preferred way to launch a JavaFX application. 2) Construct an instance of JFXPanel which will also initialize the JavaFX runtime (only do this if you don't want to construct an application instance and want to manage your own life-cycle...you may want to call Platform.setImplicitExit(false) in this case).
07-04-2014

This is a new problem and a new JIRA needs to be open to track it ��� but I'm not sure we can do much about it. JVM languages that need to be coded specially to ensure the toolkit is initialized.
07-04-2014

I don't see how we could fix this. In normal FX application, the launcher looks for the FX application class, start() and/or main() and ensures that the toolkit is initialized properly before application code can run. Here is a Java example that has the same problem as JS/Nashorn: **** PART 1 ***** package threadtest; import javafx.application.*; public class NonFXMMain { public /*final*/static Thread MAIN_THREAD; public static void main(String[] args) { try { System.out.println(Class.forName("javafx.scene.control.Control")); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } MAIN_THREAD = Thread.currentThread(); Application.launch(PrintThreads.class, args); System.out.println("DONE"); } } **** PART 2 **** package threadtest; import javafx.application.Application; import javafx.application.Platform; import javafx.stage.Stage; public class PrintThreads extends Application { private final static Thread STATIC_INIT_THREAD = Thread.currentThread(); private final Thread instanceInitThread = Thread.currentThread(); private final Thread constructorThread; private Thread initMethodThread; private Thread startupThread; public PrintThreads() { constructorThread = Thread.currentThread(); } @Override public void init() { initMethodThread = Thread.currentThread(); } // @Override public void start(Stage stage) { startupThread = Thread.currentThread(); String mainThreadId = NonFXMMain.MAIN_THREAD == null ? "n/a" : NonFXMMain.MAIN_THREAD.getId() + ""; System.out.println(mainThreadId + " - MAIN: " + NonFXMMain.MAIN_THREAD); System.out.println(STATIC_INIT_THREAD.getId() + " - static init: " + STATIC_INIT_THREAD); System.out.println(instanceInitThread.getId() +" - init: " + instanceInitThread); System.out.println(constructorThread.getId() + " - constructor: " + constructorThread); System.out.println(initMethodThread.getId() + " - init(): " + initMethodThread); System.out.println(startupThread.getId() + " - start(): " + startupThread); Platform.exit(); } public static void main(String[] args) { Application.launch(args); System.out.println("DONE"); } }
07-04-2014

As far as I can tell, the issue as it manifests in RT-33954 is still occurring. It prevents the use of JavaFX in a sane manner from dynamic languages (JS/Nashorn, Clojure). These languages will load classes via the equivalent of Class.forName(..) under the hood at times that are not guaranteed to be before launch() is called - thus causing classes extending Control to be initialized. I've cloned openjfx just recently and built it - placed the newly built jfxrt on the classpath and can very easily repro the issue (notice I added a sysout just to make sure it was using the version I built...): srazz-mba-osx:openjfx-rt % jjs -cp './build/sdk/rt/lib/ext/jfxrt.jar' jjs> Java.type("javafx.scene.control.Control"); Am I using my custom built version of Control? If we see this message than.. yes! Exception in thread "main" java.lang.ExceptionInInitializerError at java.lang.Class.forName0(Native Method) at java.lang.Class.forName(Class.java:340) at jdk.nashorn.internal.runtime.Context.findClass(Context.java:734) at jdk.nashorn.internal.objects.NativeJava.simpleType(NativeJava.java:416) at jdk.nashorn.internal.objects.NativeJava.type(NativeJava.java:247) at jdk.nashorn.internal.objects.NativeJava.type(NativeJava.java:239) at jdk.nashorn.internal.objects.NativeJava.type(NativeJava.java:235) at jdk.nashorn.internal.scripts.Script$\^shell\_.runScript(<shell>:1) at jdk.nashorn.internal.runtime.ScriptFunctionData.invoke(ScriptFunctionData.java:498) at jdk.nashorn.internal.runtime.ScriptFunction.invoke(ScriptFunction.java:206) at jdk.nashorn.internal.runtime.ScriptRuntime.apply(ScriptRuntime.java:378) at jdk.nashorn.internal.runtime.Context.eval(Context.java:475) at jdk.nashorn.tools.Shell.readEvalPrint(Shell.java:439) at jdk.nashorn.tools.Shell.run(Shell.java:155) at jdk.nashorn.tools.Shell.main(Shell.java:130) at jdk.nashorn.tools.Shell.main(Shell.java:109) Caused by: java.lang.IllegalStateException: Toolkit not initialized at com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:276) at com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:271) at com.sun.javafx.application.PlatformImpl.setPlatformUserAgentStylesheet(PlatformImpl.java:562) at com.sun.javafx.application.PlatformImpl.setDefaultPlatformUserAgentStylesheet(PlatformImpl.java:524) at javafx.scene.control.Control.<clinit>(Control.java:85)
06-04-2014

http://hg.openjdk.java.net/openjfx/8/graphics/rt/rev/9e79dc29f1a6
18-10-2013

Tested-with: ran full unit tests, including all new unit tests Several JNLP apps to verify threading of FXApplet and preloader Sanity check of other apps Unit Tests: New tests added to: launchertest/MainLauncherTest Manual tests added for deployment tests: AppLifeCycle/ThreadCheck.jnlp
18-10-2013

This patch is very simple. It runs the construction code in runAndWait(). I have run it on the desktop and in JNLP.
06-03-2013

Here is a summary of the current situation and the reasons why we might want to make this change: One of the great things about JavaFX is that it has a consistent threading model that maps well on to the underlying platforms. There is a single distinguished GUI-thread that is also the GUI-thread for the window system. This makes JavaFX GUI behavior consistent and deterministic. In general, running code in the GUI thread is a good thing for these reasons and others, provided that the code is not long running. In the case of long running code, JavaFX objects may be created in background threads as long are they are not attached to the scene graph. JavaFX applications have a life cycle that includes, construction, init(), start() and stop(). The start() method is the most important and runs in the GUI-thread. The init() method is guaranteed to run in a background thread and is the place where long running creation code can reside. The start() method will always run after init() has completed. With a preloader, the start() of the preloader runs at the same time as the init() of the application. The stop() method is also guaranteed to run in the GUI-thread. What about application construction? Today, this is unspecified and happens in a background thread. We are considering moving this to the GUI-thread.
06-03-2013

We should consider this for Lombard if we are going to make this change. Note that since this is a behavioral change we should discuss it on the openjfx alias as we would for an API change.
05-03-2013