UIThread abstraction

classic Classic list List threaded Threaded
9 messages Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

UIThread abstraction

aalmiray
Administrator
Hello everyone,

The topic of abstracting over the UI thread has surfaced a couple of times. Allow me to share my thoughts on the matter.

I think we can agree there are 3 major UI toolkits in the JVM: Swing, JavaFX and SWT. These are the toolkits that most developers are familiar with, their quirks are well understood and their communities are big enough. Next we have the lesser used/known toolkits that still deliver some punch: Apache Pivot, Lanterna and Qt (yes, Qt is well known outside of the JVM but not so much inside it). These six toolkits can be used in all major desktop platforms; I believe JavaFX and Lanterna are the only choices in embedded Java at the moment (Swing on a Raspberry Pi perhaps?). Finally we've got jGTK which runs on Linux only. There are other UI toolkits (mostly coming form the gaming community) but their usage is very limited.

All these toolkits follow simple rules:

  • there's a dedicated thread (the UI thread) where painting operations must happen.
  • widgets must be created inside the UI thread.
  • widget properties must be accessed and mutated inside the UI thread.
  • non-UI related operations must happen outside of the UI thread.

Almost all of them provide the facilities to run code inside their UI thread in a synchronous/asynchronous manner. They can identify the UI thread too. Here's the breakdown

|===
| Toolkit  | isUIThread | runSync | RunAsync
| Swing    | yes        | yes     | yes
| JavaFX   | yes        | no      | yes
| SWT      | yes        | yes     | yes
| Pivot    | yes        | yes     | yes
| Lanterna | yes        | yes     | no
|===

For some odd reason JavaFX chose not to provide a sync version, however it can be implemented using a FutureTask. This leaves Lanterna as the other odd toolkit that does not deliver async, but given its inherent nature this operation can be delivered over sync as is. I'm not counting Qt as its JVM support (Qtjambi) is lagging behind and it does not make releases so often these days.

This leads us with very good chances of defining a base abstraction over the UI thread, no matter the UI toolkit. Sven pointed out on another thread that there may be a problem with mixed toolkits as each one will use its own UI thread. Well, this is not a problem in Java8 as Swing and JavaFX can share the same thread. Turns out JavaFX and SWT can share the same thread too. Thus all 3 toolkits can share the same thread. Win! In a mixed environment one of the toolkits has to take the lead, that is, Swing components are embedded in JavaFX or viceversa. One of these toolkits initializes the UI environment and the other submits to its will.

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: UIThread abstraction

Tom Schindl
Generally speaking I'm not sure you should add synchronous/blocking APIs they should be avoided at all cost. Synchronous APIs can easily lead to deadlocks (that's the reason the JavaFX lacks this API) and blocking APIs are bad as well because they are hard to implement in Browser JavaScripts envs (yes I know we talk about Desktop & Mobile) who are async.

So whenever you design an API like this int state = window.open(); think twice if you would not be better of with window.open(Function<Integer,Boolean> handlerFunction);

Anyways for inspiration what APIs are there already this the interface defined by e4+javafx http://git.eclipse.org/c/efxclipse/org.eclipse.efxclipse.git/tree/bundles/runtime/org.eclipse.fx.ui.services/src/org/eclipse/fx/ui/services/sync/UISynchronize.java
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: UIThread abstraction

Hendrik Ebbers
Tom Schindl wrote
Generally speaking I'm not sure you should add synchronous/blocking APIs they should be avoided at all cost. Synchronous APIs can easily lead to deadlocks (that's the reason the JavaFX lacks this API) and blocking APIs are bad as well because they are hard to implement in Browser JavaScripts envs (yes I know we talk about Desktop & Mobile) who are async.
I think it's important to define a sync call because some toolkits and frameworks depend on it. I'm with you that developers should try to use callbacks instead of blocking calls.

Tom Schindl wrote
So whenever you design an API like this int state = window.open(); think twice if you would not be better of with window.open(Function<Integer,Boolean> handlerFunction);
I think we should use callbacks and non blocking calls in the JSR for example when loading or storing application states.

Tom Schindl wrote
Mostly all toolkits define the 3 base methods and most frameworks (like e4) defines more than only the 3 methods. Maybe we can split the concurrency definition of the JSR in 2 parts: Toolkit concurrency and framework concurrency. Toolkit concurrency is defined by an interface that only contains the 3 core methods. Based on this methods the framework concurrency defines some util methods that are based on the toolkit concurrency by using default implementations.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: UIThread abstraction

aalmiray
Administrator
As Hendrik notes, keeping the sync option allows full access to the underlying toolkit's UI thread. The fact that JavaFX (1 of the 6 considered toolkits) decided against this behavior should IMHO not weight too much in favor of removing said method. Sync calls are terrible in the hands of an inexpert developer, and so are async calls. I think sync calls are well understood by power developers and as such I wouldn't take it away.

It's like running with scissors; you know you'll get hurt if you do, so simply don't run or leave the scissors behind. Protecting people against running with scissors is a matter of education, not banning the usage of scissors or running ;-)
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: UIThread abstraction

johanvos
I'm not in favor.
It is much easier to add a call to the spec later than to remove a call.
As for the sync option: it is possible to do this yourself (e.g. using a Future) without it being in the spec. So it's not that you don't allow people to use this weird thing, you simply don't encourage it via the shared API.
I agree with your statement that inexpert developers can ruin everything even with async calls (also expert developers btw), but you shouldn't make it too easy to fail :)

- Johan

2015-03-03 10:46 GMT+01:00 aalmiray [via jsr377-api] <[hidden email]>:
As Hendrik notes, keeping the sync option allows full access to the underlying toolkit's UI thread. The fact that JavaFX (1 of the 6 considered toolkits) decided against this behavior should IMHO not weight too much in favor of removing said method. Sync calls are terrible in the hands of an inexpert developer, and so are async calls. I think sync calls are well understood by power developers and as such I wouldn't take it away.

It's like running with scissors; you know you'll get hurt if you do, so simply don't run or leave the scissors behind. Protecting people against running with scissors is a matter of education, not banning the usage of scissors or running ;-)


If you reply to this email, your message will be added to the discussion below:
http://jsr377-api.40747.n7.nabble.com/UIThread-abstraction-tp100p112.html
To start a new topic under jsr377-api, email [hidden email]
To unsubscribe from jsr377-api, click here.
NAML

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: UIThread abstraction

Tom Schindl
I agree with Johan - as long as there's the async one available one can implement a sync one, e.g. by providing an additional library outside the JSR scope people can make use of, similar to what Hendrik did for javafx and we did as well as part of e(fx)clipse.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: UIThread abstraction

aalmiray
Administrator
Fair enough. I think we can begin fleshing out thew API without a sync abstraction and see how far we go.
I still think there will be cases where a developer really needs to issue a sync call, in which case he or she will have to either

  1) access the explicit sync support delivered by the toolkit (i.e, SwingUtilities.invokeAndWait)
  2) create an abstraction over JSR377's UI thread abstraction that delivers sync calls (leveraging #1)

I think it's best if we move forward and identify decision points that can be deferred (such as sync calls) until we definitely have to make the final call.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: UIThread abstraction

Sébastien Bordes
In reply to this post by Tom Schindl
I'm agree too, if you add a sync method into the API, all developer will think this is a "recommended" way and will abuse of it with all bug that it can generates
 and that are so difficult to diagnose (like programmatic deadlock )

2015-03-03 11:03 GMT+01:00 Tom Schindl [via jsr377-api] <[hidden email]>:
I agree with Johan - as long as there's the async one available one can implement a sync one, e.g. by providing an additional library outside the JSR scope people can make use of, similar to what Hendrik did for javafx and we did as well as part of e(fx)clipse.


If you reply to this email, your message will be added to the discussion below:
http://jsr377-api.40747.n7.nabble.com/UIThread-abstraction-tp100p116.html
To start a new topic under jsr377-api, email [hidden email]
To unsubscribe from jsr377-api, click here.
NAML

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: UIThread abstraction

aalmiray
Administrator
FYI JavaFX does provide a runAndWait() method, but it's hidden from view. See http://hg.openjdk.java.net/openjfx/8u-dev/rt/file/f9a79df8559d/modules/graphics/src/main/java/com/sun/javafx/application/PlatformImpl.java#l306

This class is used by the Launcher to call the start() method of an application

----
                PlatformImpl.runAndWait(() -> {
                    try {
                        startCalled.set(true);

                        // Create primary stage and call application start method
                        final Stage primaryStage = new Stage();
                        primaryStage.impl_setPrimary(true);
                        theApp.start(primaryStage);
                    } catch (Throwable t) {
                        System.err.println("Exception in Application start method");
                        startError = t;
                        error = true;
                    }
                });
----
Loading...