0

While debugging an application I would like the main thread to wait after each Runnable I put on the JavaFX event queue using

Platform.runLater(new Runnable()... )

to wait until it has been executed (i.e. is visible). However there are two twists here:

First, it is not really a standard, GUI driven JavaFX app. It is rather a script showing and updating a JavaFX stage every now an then. So the structure looks something like this:

public static void main(String [] args){
    //do some calculations
    SomeView someView = new SomeView(data); //SomeView is basically a wrapper for  a stage
    PlotUtils.plotView(someView) //displays SomeView (i.e. the stage)
    //do some more calculations
    someView.updateView(updatedData)
    //do some more calculations
}

public class SomeView {
    private static boolean viewUpdated = false;
    private ObservableList<....> observableData;
    public void updateView(Data data){
        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                observableData.addAll(data);
                boolean viewUpdated = true; 
            }
        });
    //If configured (e.g using boolean switch), wait here until
    //the Runnable has been executed and the Stage has been updated.
    //At the moment I am doing this by waiting until viewUpdated has been
    //set to true ... but I am looking for a better solution!
    }
}

Second, it should be easy to disable this "feature", i.e. to wait for the Runnable to be executed (this would be no problem using the current approach but should be possible with the alternative approach as well).

What is the best way to do this?

E.g. is there something like a blocking version to execute a Runnable on the JavaFX thread or is there an easy way to check whether all events on the event queue have been executed/ the eventqueue is empty....?

Ueli Hofstetter
  • 2,409
  • 4
  • 29
  • 52
  • What is your definition of "main thread"? JavaFX has it's own [application thread](https://docs.oracle.com/javase/8/javafx/api/javafx/application/Application.html) and this is different from the application thread traditionally used to execute the `main(String[] args)` method of a program. You pretty much never want to have the JavaFX application thread to wait because then the whole application will freeze (e.g. none of the runnables you dispatched via Platform would ever be executed and no other operations such as painting or input handling would be handled by the application). – jewelsea Apr 30 '15 at 11:58
  • I don't want the JavaFX thread to wait, I want the main thread (or in general the thread which calls "Platform.runLater") to wait. – Ueli Hofstetter Apr 30 '15 at 12:19
  • To clarify, you mean you have started your own background thread somewhere, from which you are calling `Platform.runLater(...)`. You want this background thread to wait until the runnable passed to `runLater(...)` is complete, is that correct? – James_D Apr 30 '15 at 13:00
  • Yes essentially this is what I mean... except for that it will not necessarily be a background thread (i.e. it could be the "main" thread as well). for now I am using my "solution 1" and it works as intended... however, I guess this is not really the way to go .... so I am looking for better solutions. – Ueli Hofstetter Apr 30 '15 at 13:05
  • OK, so you haven't answered @jewelsea's question: what do you mean by "main thread"? The only interpretation of "main thread" in the context of a JavaFX application is the JavaFX application thread itself. Obviously if you block that under these circumstances, you will get deadlock. – James_D Apr 30 '15 at 13:08
  • Thanks for the support. I finally got what you guys mean. Sry, my bad. I updated the question ... hopefully it is clear now, otherwise please let me know. thx. – Ueli Hofstetter Apr 30 '15 at 13:45

2 Answers2

2

There's also PlatformImpl.runAndWait() that uses a countdown latch so long as you don't call it from the JavaFX thread

J. Dimeo
  • 262
  • 2
  • 10
1

This is based on the general idea from JavaFX2: Can I pause a background Task / Service?

The basic idea is to submit a FutureTask<Void> to Platform.runLater() and then to call get() on the FutureTask. get() will block until the task has been completed:

// on some background thread:

Runnable runnable = () -> { /* code to execute on FX Application Thread */};
FutureTask<Void> task = new FutureTask<>(runnable, null);
Platform.runLater(task);
task.get();

You must not execute this code block on the FX Application Thread, as this will result in deadlock.

If you want this to be easily configurable, you could do the following:

// Wraps an executor and pauses the current thread 
// until the execution of the runnable provided to execute() is complete

// Caution! Calling the execute() method on this executor from the same thread
// used by the underlying executor will result in deadlock.

public class DebugExecutor implements Executor {
    private final Executor exec ;

    public DebugExecutor(Executor executor) {
        this.exec = executor ;
    }

    @Override
    public void execute(Runnable command) {
        FutureTask<Void> task = new FutureTask<>(command, null);
        exec.execute(command);
        try {
            task.get();
        } catch (InterruptedException interrupt) {
            throw new Error("Unexpected interruption");
        } catch (ExecutionException exc) {
            throw new RuntimeException(exc);
        }
    }
}

Now in your application you can do:

// for debug:
Executor frontExec = new DebugExecutor(Platform::runLater);
// for production:
// Executor frontExec = Platform::runLater ;

and replace all the calls to Platform.runLater(...) with frontExec.execute(...);

Depending on how configurable you want this, you could create frontExec conditionally based on a command-line argument, or a properties file (or, if you are using a dependency injection framework, you can inject it).

Community
  • 1
  • 1
James_D
  • 201,275
  • 16
  • 291
  • 322
  • This sounds great. I was thinking about using something like future as well... but could not figure out how to implement it. I will give it a try. – Ueli Hofstetter Apr 30 '15 at 13:54