/ Desktop Application Framework (JSR 377) / Concurrency in UI Toolkits (Part 2)

Concurrency in UI Toolkits (Part 2)

Hendrik on 2015/02/01 - 00:24 in Desktop Application Framework (JSR 377), JavaFX

In the first post of this series I showed how Concurrency is handled in UI Toolkits and how a generic approach to work with the toolkit specific thread may look like. This ends in the following interface:

But there are still some problems with this interface:

  • What happens if the runOnUiToolkitThreadAndWait(..) method is called on the UI-Thread?
  • One method returns a Future<> that defines the “boolean cancel(boolean mayInterruptIfRunning)” method. By definition thy method will interrupt the thread in that the task is running. But we never want to interrupt the UI Toolkit thread.

Let’s start with the first problem. Before we can solve this another question must be answered: I it’s a problem to call this methods on the toolkit thread we need a way to check if the current thread is the toolkit thread. To do so most toolkits provide a helper method that checks if the current thread is the toolkit Thread. Examples are shown in the following code snippet.

Because most Toolkits support this method we can simply add it to our interface:

Once this is done we can have a deeper look at the methods that will block until a task was executed on the ui toolkit. In the defined interface the two methods that are named “runOnUiToolkitThreadAndWait” defines this behavior. Once the method is called a new task is created and added to the ui thread. Because the thread has a lot of work to do normally a queue will handle this tasks and execute them by using a first in first out approach. The following image shows an example.

queue

By doing so our task will be added to the queue and executed once all task that has been added earlier to the queue were executed. If we call this method from the ui thread the created task can’t be executed before the task that is currently running is finished. But because the “runOnUiToolkitThreadAndWait” methods will wait for the execution of the new task we will end in an deadlock that is definitely the worst think that can happen. By doing so nothing can be handled on the UI thread: No user interaction or rendering can be done and the application is frozen. Because no Exception will be thrown the application just hangs we will receive no information what has triggered the error.
With the help of the new “isUIToolkitThread()” method we can avoid this behavior and refactor the methods to an more fail-safe version. With a simple if-statement we can add a special behavior if the “runOnUiToolkitThreadAndWait” method is called from the ui thread:


Once this is done we need to decide what we want to do if the method was called join the ui thread. In general there are two different ways how this is handled by ui toolkits:

  • throw an Exception (checked or unchecked)
  • directly execute the runnable by calling the run() method

Here are the implementations for this approaches:

The first 2 methods looks mostly the same. Only the exception type is different. The first method uses an unchecked exception that will end in a more readable code when using the method because you don’t need to catch the new exception type all the time. But developers need to know that an unchecked exception will be thrown whenever the method is called on the ui thread to avoid errors at runtime.
The third method can be called on any thread. A developer doesn’t need to think about it. If you know what you do, this is the most flexibel way how such a method can be defined. But on the other hand this can cause problems. I have seen a lot of projects where developers used this type of method way to often. Because they ad no idea how to handle the ui thread invokeAndWait(..) methods were called all over the code. By doing so your stack trace ends in something like this:

stack

This will end in code that is unperformant, unstable and can’t be maintained anymore. Therefore I would choose one of the first 2 implementations. But that’s only how I see this things and maybe you have a complete different opinion. Therefore it would be great if you can leave a comment here about your favorite way how to handle this problems. JSR-377 will contain such a interface and we want to resolve all the shown problems in an ui toolkit independent way. If you are interested in the JSR or want to share your opinion about this topic you should have a look at the JSR Mailing List. In the next post I will have a deeper look at the Future<> interface in combination with ui threads.

15 POST COMMENT

Send Us A Message Here

Your email address will not be published. Required fields are marked *

15 Comments
  • 2015/02/01

    Hi Hendrik,

    my preference would be to not provide blocking calls at all. I would change the signature of runOnUiToolkitThread to return CompletionStage instead of Future:

    CompletionStage runOnUiToolkitThread(Callable callable);

    CompletionStage API does not allow the user to block and wait for the result, but to execute an action asynchronously when the result becomes available. This approach prevents deadlocks and uses the threads more efficiently.

    Tomas Mikula
    Reply
    • 2015/02/01

      Thanks Tomas,

      I will have a deeper look at CompletionStage. I don’t know if all methods of the interfaces matches to the ui thread. It’s ,mostly designed like the ProcessChain API that I created for DataFX. Currently I don’t know if the interface matches perfect to the workflows that you want to define when working with the ui thread. maybe you have more knowledge and provide a deeper look. On the other hand I don’t know if such a complex interface is the right return type in this low level interface. Until know I was thinking about a high level API (like ProcessChain or CompletionStage) that is based on top of this low level API / interface.
      About blocking methods: When using Java 8 and Lambdas I would prefer reactive programming, too. But I don’t know if everyone wants to use it. And therefore a basic blocking methods isn’t a bad idea. I think it’s important to provide both approaches in a common and basic way. Based on this I would love to have a reactive API that only uses the non blocking methods.

      Reply
      • 2015/02/01

        > Until know I was thinking about a high level API (like ProcessChain or
        > CompletionStage) that is based on top of this low level API / interface.

        CompletionsStage is a rich API, whose implementations, e.g. CompletableFuture, are implemented on top of a lower API like Executor.

        Looks like it has a lot of overlap with your thinking 😉

        Tomas Mikula
        Reply
      • 2015/02/01

        > About blocking methods: When using Java 8 and Lambdas
        > I would prefer reactive programming, too. But I don’t know
        > if everyone wants to use it. And therefore a basic blocking methods
        > isn’t a bad idea. I think it’s important to provide both approaches
        > in a common and basic way.

        I think the decision should really be driven by people’s use cases. In my limited experience, I always ended up being better with non-blocking reactive behavior, but other people may step in and show a compelling example where blocking is really desirable.

        Tomas Mikula
        Reply
    • 2015/02/01

      In the CompletionStage interface methods like acceptEitherAsync(CompletionStage< ? extends T> other, Consumer< ? super T> action, Executor executor) are defined. This is great for general multi threading but when working with the ui thread I want methods like:

      • acceptEitherAsyncOnUiThread(CompletionStage< ? extends T> other, Consumer< ? super T> action)
      • acceptEitherAsyncOnBackgroundThread(CompletionStage< ? extends T> other, Consumer< ? super T> action)
      • acceptEitherAsyncOnExecutor(CompletionStage< ? extends T> other, Consumer< ? super T> action, Executor e)

      Normally the implementation of the interface internally manages an executor that will be a thread pool for example. The consumer that is defined in the second method will be called on this executor. The first methods will execute the consumer on the ui thread of the underlying ui toolkit. In addition the third method is more general. here you can provide your own executor. Maybe the ui toolkit will provide a method that extends CompletionStage and provides addition methods that are defined for the ui thread.

      Reply
      • 2015/02/01

        Maybe a UiToolkitExecutor interface will be helpful that extends the Executor interface and automatically calls all Runnable task on the ui thread. By doing so the CompletionStage can be used internally to provide a reactive API. Just thinking :)

        Reply
        • 2015/02/01

          You don’t really need another interface extending Executor, just an instance of Executor that executes actions on the UI thread. For JavaFX, this is

          Executor UI_TOOLKIT_EXECUTOR = Platform::runLater;

          Tomas Mikula
          Reply
      • 2015/02/01

        In addition we need to check if a general approach can be based on Java 8 today. For Swing and JavaFX this will work without any problems but RoboVM and Android (JavaFXPorts) will provide problems. In addition I don’t know if SWT is running good on Java 8 today.

        Reply
      • 2015/02/01

        acceptEitherAsyncOnUiThread(CompletionStage other, Consumer action)

        This is

        acceptEitherAsync(other, action, Platform::runLater)

        acceptEitherAsyncOnBackgroundThread(CompletionStage other, Consumer action)

        This is

        acceptEitherAsync(other, action)

        (Omitting the executor runs it on the default executor.)

        acceptEitherAsyncOnExecutor(CompletionStage other, Consumer action, Executor e)

        This is

        acceptEitherAsync(other, action, e)

        Tomas Mikula
        Reply
        • 2015/02/01

          OK, how do I prevent the commenting system from removing the angle brackets from generics and formatting blockquotes as quoted text?

          Tomas Mikula
          Reply
          • 2015/02/01

            I mean make it format blockquotes as quoted text.

            Tomas Mikula
          • 2015/02/01

            Maybe add something like

            blockquote { padding-left: 10px; border-left: 2px solid lightgray; }

            to your stylesheet?

            Tomas Mikula
        • 2015/02/02

          acceptEitherAsync(other, action, Platform::runLater) < - That's right but I don't want user to type this. therefore I'm against using the CompletionStage interface as public API here. Internally this will definitely work.

          Reply
          • 2015/02/02

            The question is, whether any user would ever want to do something like this, that is to do

            runOnUiThread(callable).thenAcceptAsyncOnUiThread(consumer)

            These are two subsequent actions (callable and consumer) executed on the UI thread and we just introduced some extra overhead and asynchrony between them. In this case, the user would be better off with

            runOnUiThread(() -> consumer.accept(callable.call()))

            Tomas Mikula
          • 2015/02/02

            It is also important to keep in mind that third party asynchronous/concurrent libraries will return CompletionStage or something else, but certainly not anything UI related. Thus the existence of UI-related completion stage/process chain interface will not help those users at all.

            Tomas Mikula