JDK-8124368 : Problems with Application static objects and pipeline initialization
  • Type: Bug
  • Component: javafx
  • Sub-Component: graphics
  • Affected Version: 8
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2013-02-22
  • Updated: 2015-06-17
  • Resolved: 2013-03-01
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
Blocks :  
Duplicate :  
Relates :  
Relates :  
Relates :  
Relates :  
Description
Recent changes to the code base have added restrictions of creating Application static objects.
We need to understand and determine how to proceed with this, with a few options being:
 * document the restriction ie. no Application static Controls/Images
 * change the initialization of the pipeline, moving pipeline init into Application static initialization.
 * change the known issues causing the limitiation ie removing the calls that get Screen prior to that data being avilable
    + in Utils.isQVGA (which is isEmbedded)
    + in Image creation.
  it might be possible to lazy evaluate the data, deferring the operations until on the event thread, and hence when the pipeline is initialized.

The issue boiled into a code fragment:
  public class ChangeScene1 extends Application {
    static com.sun.glass.ui.Screen screenStatic = com.sun.glass.ui.Screen.getMainScreen(); <<<<<<< FAILS
    com.sun.glass.ui.Screen screenInstance = com.sun.glass.ui.Screen.getMainScreen(); <<<<<<< Works 
Comments
I think this needs to be split into separate JIRA issues (I also discussed this with Steve). The specific problem described by the summary and description of this JIRA issue is already addressed by the Java 8 launcher changes implemented in the JDK by https://jbs.oracle.com/bugs/browse/JDK-8001533 and activated when jfxrt.jar was moved into lib/ext by RT-26125. Note that Glass is initialized before the launcher loads and constructs the concrete subclass of Application. This has always been the case. Prior to Java 8, the only portion of an app that could possibly run before Toolkit startup (which is where Glass is initialized) was the static initializer of an application if that application was built without using the javafx packager. With the Java 8 launcher changes, the Toolkit startup will now always happen before loading the Application class even for applications that are built without using the javafx packager. For the follow-on work, I have created the following JIRA issues, all of them are Tweaks. I believe this captures all of the work being discussed in this JIRA, but if I missed something we can always file another one. RT-28754: 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 rather than a background thread. RT-25485: Eliminate the redundant JavaFX-Launcher thread for standalone applications (I had previously filed this). RT-28755: Consider always calling the main() method for standalone apps The following is a longer term issue (and more involved): RT-28756: Consider using the Java main thread as the FX application thread
01-03-2013

Marking this issue as resolved after having filed the follow-on JIRAs.
01-03-2013

The patch was to illustrate a simple change that would not make our implementation better but would remove one more thread that the application programmer normally sees (ie. most applications don't use init()). It was not fully tested other than to see that the right threads were printed out and Ensemble ran. If we decide that exposing one less thread to the application programmer is a good thing, it is a simple enough fix to our current code base and we can go ahead and test all the cases using the approach in the patch. Agree? Long term is another discussion.
28-02-2013

It seems like any risk would be minimal and in any case we've got a while to work out any edge cases that come up. I think it becomes us to make things simpler, smaller, and faster whenever we can (we ought not leave things in a less than ideal state just because that happened to be the first implementation we shipped). There are several problems we're trying to address here. The first is that our platform is more complicated than it needs to be, and therefore we ought to simplify it. This is good for maintenance if nothing else. The second problem is that 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). So I think actually there are problems for which we're providing solutions :-)
28-02-2013

I'm still unconvinced that this is useful enough to justify the change in behavior or the risk that this might introduce. Seems like a solution in search of a problem at this point. I'll take a look but at a minimum it is incomplete because it doesn't address the deploy cases.
28-02-2013

Ignoring Java main(), this patch runs everything else in the UI thread except for init(). I did not test/try the pre-loader. Do we have example code for that? The patch does not address the issue that start is complex and involves many threads. A quick attempt to reduce the number of threads to one leads to a crash in Glass (not caused by any sort of operating system problem because all operating system calls are valid at this point). There is likely a variable in C that need to be initialized but was not.
28-02-2013

> I'm not sure I fully understand all of the implications of the above. At first glance, it seems like you are proposing a bunch of work, but it isn't clear to me what the benefit of these changes are. > > In particular: > > 1) It is already the case (in JDK 7 / FX 2.x) that main() is not called for applications packaged up with the javafx packager. It is also not called for Web Start applications (or applets). Also, the new Java 8 launcher doesn't call main() for FX application classes launched directly. There was a reasonably long discussion about this when the Java 8 launcher feature was being discussed. I disagree that we should always call main, since we already don't / can't in all modes. I think the best way to achieve consistency is to never call main, which is what the current Java 8 launcher finally gets us to. I think that is a reasonable argument to make (by never calling main the developer is taught that it will not be called in some contexts). On the other hand: a. IDE's won't know that it is launchable (for some time) b. Developers will expect it (even though it wasn't called in other cases like applets) Have we discussed with the different IDE vendors that they need to recognize an Application as being launchable? In any case, this point 1 I think is orthogonal to the threading issues and is more a side-note. My expectation was that if main was there, it would be called, to be consistent with existing programs (because not everybody uses javafxpackager). > 2b) This would be a technically incompatible change for seemingly little benefit. At a minimum it will require changes in deploy code and the FX launcher code, but wouldn't be too hard to do if there was a good reason. And I think it is independent of any of the other changes proposed here. Right, it is technically behaviorally incompatible, and that is the main reason I'm interested in it. It is my view (and has always been my view) that the fewer threads that a naive application encounters the better -- and in fact, unless the developer goes out of their way, they should ONLY EVER encounter the FX thread. Now that we have the Java launcher ensuring that the FX thread is fired up before the application is loaded, we have the perfect opportunity to realize this. public class MyApp extends Application { private final Thread STATIC_INIT_THREAD = Thread.currentThread(); private Thread MAIN_THREAD; private final Thread initializationThread = Thread.currentThread(); private final Thread constructorThread; private Thread initThread; private Thread startupThread; public MyApp() { constructorThread = Thread.currentThread(); } @Override public void init() { initThread = Thread.currentThread(); } @Override public void startup(Stage stage) { startupThread = Thread.currentThread(); System.out.println(STATIC_INIT_THREAD); System.out.println(MAIN_THREAD); System.out.println(initializationThread); System.out.println(constructorThread); System.out.println(initThread); System.out.println(startupThread); } public static void main(String[] args) { MAIN_THREAD = Thread.currentThread(); } } I believe this app (haven't run it) on 2.X would report: Main Main Launcher Launcher Launcher FX App And from this discussion I believe on 8 it would report: Launcher Launcher Launcher Launcher Launcher FX App But what I would like it to report is: FX App FX App FX App FX App Launcher FX App The reason is that, in 2.x, every Application (naive or sophisticated) is actually being executed via 3 threads, and at times the developer will encounter this (although somewhat rarely). In 8 as planned, every application (naive or sophisticated) will be hit via 2 threads, and at times the developer will encounter this. Instead, I'd prefer that for any naive application (that is, one that doesn't use init) we have *all* developer code only execute on the FX thread. The only way an application encounters a different thread from the FX thread is if they take some explicit action to utilize multiple threads -- use a Worker, override init, or kick off their own threads / executors. This means no surprises (well, at least, no surprises we can be blamed for). The drawback is that any time spent initializing the class file is done on the FX thread instead of a background thread. This is only a potential issue for the preloader case. However the application already has to rework their Application to move code into init from start if they're going to use a preloader to good effect, and it seems reasonable that for such applications they also should move any field / constructor work into init as well. So the only work is the actual class loading of the Application class on the FX thread to ensure that the static initializers are called on the FX thread. So I think that is the essential tradeoff -- is the class loading overhead enough to cause a hiccup in the preloaded, or not? > 3) We aren't really going to reduce the number of threads to 1 ever (the JVM has a couple threads for disposal, GC, etc., and FX has the renderer thread and a thread per media stream), but I guess you mean the number of threads that an application has to know about? Really I just meant reducing from 3 persistent threads to 1 (main, launcher, fx -> fx (with ephemeral launcher thread(s))). > With the new Java 8 launcher we are already there. I don't see what this buys us really given that you propose to keep the init() method running on a separate thread. With Java 8 there are only two threads the user ever sees: the thread that runs init() (and, unless changed in 2b, Application construction), and the FX app thread that runs everything else. Right with 8 I think we already went from 3 to 2 (launcher & fx threads would continue to live forever)? > Note that from a startup point of view, we can trivially get rid of the one extra thread that we do have so we are down to only the two that we need. I think that's the contention, that actually we could get it down to just the FX thread that lives forever. > 3c) I don't know what you mean by "This implies FX Thread must be the GUI thread" since those two terms are used interchangeably. Yes sorry, by GUI thread I mean the c-main thread on Mac (the Cocoa GUI thread). I believe that the only reason you need the long-lived launcher thread is because the initialization happens on something other than the cocoa GUI thread, and so we need another thread in Java that can be tied to the cocoa native thread. We could skip all that and instead just have the Java launcher avoid creating the launcher thread. So the idea is, the Java launcher goes "oh, hey! I'm running an Application, so I'll just not bother forking off another thread, I'll just initialize on the c-main thread". Our initialization then just says "I'll create this application object, and then I'll spin off an "init" thread and call init() on the application and wait until it completes, and then I'll call start and then I'll tell glass to start pumping events". On mac the glass call returns right off since cocoa pumps the events automatically instead of us doing it manually -- on other platforms we pump the events within that glass call and once that concludes, control concludes and the VM quits normally. > 4) Will there be any problems achieving this? I don't think so, but we'd have to try it to find any unknown issues I would guess.
26-02-2013

1) We are currently calling main() and we are proposing to no longer call it on the desktop. This is a breaking change. I don't see the problem with calling main() providing it exists. Programmers are used to main() not being called on the web. 2b), 3) Devils advocate: Explain how the current threading and start up structure is either faster, smaller or easier to understand. The current architecture has 3 threads (main, launcher, gui). These exist forever. The new architecture would have one thread (gui) and one temporary thread (init). 3c) There will only be one thead at runtime that is the gui thread (which by definition is the FX Application thread). 4) There should be no problems. AWT/Swing will start up the same way it normally starts and forward mouse/keyboard etc. and FX will give back pixels. This is how it works now.
26-02-2013

I'm not sure I fully understand all of the implications of the above. At first glance, it seems like you are proposing a bunch of work, but it isn't clear to me what the benefit of these changes are. In particular: 1) It is already the case (in JDK 7 / FX 2.x) that main() is not called for applications packaged up with the javafx packager. It is also not called for Web Start applications (or applets). Also, the new Java 8 launcher doesn't call main() for FX application classes launched directly. There was a reasonably long discussion about this when the Java 8 launcher feature was being discussed. I disagree that we should always call main, since we already don't / can't in all modes. I think the best way to achieve consistency is to never call main, which is what the current Java 8 launcher finally gets us to. 2b) This would be a technically incompatible change for seemingly little benefit. At a minimum it will require changes in deploy code and the FX launcher code, but wouldn't be too hard to do if there was a good reason. And I think it is independent of any of the other changes proposed here. 3) We aren't really going to reduce the number of threads to 1 ever (the JVM has a couple threads for disposal, GC, etc., and FX has the renderer thread and a thread per media stream), but I guess you mean the number of threads that an application has to know about? With the new Java 8 launcher we are already there. I don't see what this buys us really given that you propose to keep the init() method running on a separate thread. With Java 8 there are only two threads the user ever sees: the thread that runs init() (and, unless changed in 2b, Application construction), and the FX app thread that runs everything else. Note that from a startup point of view, we can trivially get rid of the one extra thread that we do have so we are down to only the two that we need. 3c) I don't know what you mean by "This implies FX Thread must be the GUI thread" since those two terms are used interchangeably. 4) Will there be any problems achieving this?
25-02-2013

OK! That is great. Steve and I had a call today and I think this makes sense: 1. main() should be called if it exists because: a. IDE's won't know that it is launchable (for some time) b. Developers will expect it (even though it wasn't called in other cases like applets) 2. init() must be called on a background thread a. We want the same threading behavior whether application is created via preloader or not b. We want constructor, static init, etc to be called on the FX thread i. The idea is to reduce the number of threads a naive app ever encounters 3. Reduce threads to 1! a. "main" is FX Thread b. Create short-lived temporary init thread for calling "init" method on applications c. This implies FX Thread must be the GUI thread i. Note this also means that SWT embedded does not need to use -XstartOnFirstThread if SWT main is on an Application class 4. Swing Embedding a. Should still work the same as today 5. Swing EDT on FX Thread? a. If AWT can run on Cocoa main thread, then we're done. i. Developer would have to opt in by way of a flag This means, we have to change the java launcher, possibly the applet launcher (perhaps not major changes). We simplify the application model for simple apps, and we reduce the number of threads and we are able to simplify Glass code and the LauncherImpl code, and we simplify usage for embedding FX in SWT.
25-02-2013

Background: Let's talk about GUI threads and non-GUI threads. The GUI thread is the thread that runs the event loop. A non-GUI thread is any other thread. Let's also talk about Java main() and C main() to distinguish entry points into the application. In native applications, C main() is almost always also the GUI-thread. This is enforced on the Mac. There can only be one GUI thread and that thread runs C main(). In Java on the Mac, Java main() is run in a non-GUI thread unless -XstartOnFirstThread is specified. On other platforms, it doesn't matter what thread Java main() is run in because any thread can become a GUI-thread. The Problem: Java code can run in a non-GUI thread before the JavaFX toolkit has been initialized and the GUI thread has been started. Certain operating system calls are illegal on non-GUI threads, while others are valid. Let's call the valid ones "graphics calls". For those calls that are valid, the JavaFX embedded implementation requires that the graphics system be initialized to support the calls that are valid. Full Solutions: 1) Allow the full Toolkit to be initialized from a non-GUI thead 2) Allow only the porition of the Toolkit that is needed to make graphics calls to be initialized from a non-GUI thread 3) Force Java main() to be the GUI thread and initialize the full Toolkit freom there 4) Run Java code before Java main() and initialize the Toolkit from there Note that there is no solution that will support operating system calls that are illegal on non-GUI threads. Also, *all solutions that attempt to initialize the toolkit before Java main() are partial solutions*. For example, inialization of non-static fields is supported today by creating the instance of the Application class in a launcher thread. This thread runs after the toolkit has been initialized. Implementations of graphics calls may or may not cache their results. However, at some point, in a full solution, they must make an operating system graphics call from a non-GUI thread to prime the cache.
25-02-2013

Yes, this will work from an IDE, too. In all cases not just when using the javafx pacakger. Java 8 recognizes class files that extend Application and launches them differently.
25-02-2013

Is that the case when launched from within an IDE as well? How does Java 8 have it so that the toolkit is initialized before the class is loaded (is this only working correctly when javafx packager was used, or in all cases when using that java command?)
25-02-2013

I verified that for Java 8, which is our supported platform, we don't have the issue with static variables being initialized too early. Before any user class files are loaded, the Java launcher starts up the FX Toolkit. Note that this prevents any issues associated with calling methods that require the toolkit to be started up. There are still operations that are illegal from a thread other than the application thread. Static fields and instance fields of the application class are not run on the GUI thread, so only those operations that are legal on an arbitrary thread are legal to do is a static or instance field.
25-02-2013

Here is some test code that captures the cases: package test; import javafx.application.*; import javafx.collections.*; import javafx.scene.*; import javafx.scene.control.*; import javafx.scene.image.*; import javafx.scene.layout.*; import javafx.stage.*; public class TestStartUp extends Application { // Static initialization (runs in Java main() thread) static Button button1; static Image image1; static ObservableList<Screen> screens1; static { button1 = new Button ("Button1"); //image1 = new Image(TestStartUp.class.getResource("fred.gif").toString()); //screens1 = Screen.getScreens(); } // Instance initialization (runs in JavaFX launcher thread) Button button2; Image image2; ObservableList<Screen> screens2; { button2 = new Button ("Button2"); image2 = new Image(TestStartUp.class.getResource("fred.gif").toString()); screens2 = Screen.getScreens(); } // Program start (runs in Java main() thread) public static void main(String[] args) { launch(args); } // GUI application start (runs in JavaFX application thread) public void start(final Stage stage) { VBox box = new VBox (); final Button button3 = new Button ("Button3"); box.getChildren().addAll(button1, button2, button3); stage.setScene(new Scene(box)); stage.show(); } }
25-02-2013

Note that it isn't just a question of making it work on embedded versus not, it currently doesn't work on desktop either depending on which objects you instantiate in the static initializer. Btw, if we explicitly disallowed it (option #1) by specification, that seems OK, given that it is discouraged, except for the concern about breaking backward compatibility for some existing apps.
23-02-2013

I don't see why we necessarily need to make static creation work on embedded -- it seems more to me like luck that we haven't run into this problem before. But I would like to know more of the other options mentioned as they might resolve this more elegantly than just chasing regressions as they come up (like the Screens business).
22-02-2013

> * document the restriction ie. no Application static Controls/Images This will likely be seen as a functional regression. While we have never encouraged creating objects prior to the construction of the application object, we have not forbidden it either. Still, it would be a better choice than... > * change the initialization of the pipeline, moving pipeline init into Application static initialization. This would be a pretty fundamental change in our application life-cycle. I do not think this is a good idea at all. > * change the known issues causing the limitiation ie removing the calls that get Screen prior to that data being avilable This seems like the best approach to me. And it may not even be needed for Java 8. With the java launcher changes, the Application class shouldn't be loaded / initialized until the pipeline is running.
22-02-2013