1

I've got an asynchronous C++ function that needs to pass work to another thread and then wait for that work to finish. I've done this using a std::promise object, like so:

void task(std::function<void()> const& func) {
    std::promise<void> promise;
    //You can think of 'thread_pool' as being a wrapper around a std::vector<std::thread>
    //where all the threads have a body that more-or-less look like
    /* void run() {
     *     while(running) {
     *         task t;
     *         if(task_queue.try_pop(t)) t();
     *     }
     * }
     */
    thread_pool.post([&] {
        try {
            func();
            promise.set_value();
        } catch (...) {
            promise.set_exception(std::current_exception());
        }
    });

    promise.get_future().get();
}

So my question is, what is the simplest way to express the same concept in Java? In my specific case, I need to manage communication between a Swing thread and a JavaFX thread, and manage tasks between the two. This is what I have so far:

public static void runAndWait(Runnable runner) {
    Future<Object> future = new FutureTask<>(new Callable<Object>() {
        public Object call() {
            try {
                runner.run();
            } catch (RuntimeException e) {
                //??? How do I report the exception to the future?
            }
            return null;
        }
    });

    Platform.runLater(/*How do I run the future I've just created?*/);

    future.get();//I want the exception to throw here if we caught one.
}

Obviously, though, I'm missing a few things. How can I express the C++ code I described in Java?

Baum mit Augen
  • 49,044
  • 25
  • 144
  • 182
Xirema
  • 19,889
  • 4
  • 32
  • 68
  • @Baummitaugen Why was the c++ tag removed? Answering this question is going to require familiarity with the C++ code described in the first half of the question. – Xirema Mar 20 '17 at 15:44
  • 2
    I would remove the c++ code, just include the java that you have, describe what you want it to do, and what it's currently doing. That would be a higher quality question – Cruncher Mar 20 '17 at 15:45
  • 2
    @Xirema Because the question is not about C++. It will not help anyone with a question like *"How do I do foo in C++?"*. It's purely about how to do foo in Java. – Baum mit Augen Mar 20 '17 at 15:46

3 Answers3

2

You're focusing on the wrong thing. Although a Future can support the behavior you want -- waiting for the computation to complete -- that's just the semantics of an ordinary method invocation. The point of a Future is to represent the completion of an asynchronous task that you can check / retrieve later, after doing other work. You don't need to implement the whole contract of Future.

The main thing you seem to be struggling with is how to determine when the JavaFX thread has finished performing the computation. As far as I know or can determine, JavaFX has no specific interface for that; it is designed around the concept idea that the JavaFX is overall managing thread of the application. If you want to do work on that thread and be informed when it's done, then performing the notification needs to be part of the work.

For example,

public static void runAndWait(Runnable runner) {
    final SynchronousQueue<RuntimeException> exceptionQueue = new SynchronousQueue<>();

    Platform.runLater(
        // This Runnable wrapper performs synchronization with the invoking
        // thread via the SynchonousQueue
        new Runnable {
            public void run() {
                try {
                    runner.run();
                    exceptionQueue.put(null);
                } catch (RuntimeException re) {
                    exceptionQueue.put(re);
                }
            }
        });

    // blocks until an element is inserted into the queue:
    RuntimeException re = exceptionQueue.take();

    if (re != null) {
        throw new RuntimeException(re);
    }
}

You're going at it from the wrong end. Start with the job submission interface; the appropriate Java interface for this is ExecutorService. Java has several implementations, including two variations on a thread pool, and one abstract implementation that you should be able to customize to run tasks on the existing thread of your choice. You probably don't need to implement Future at all; instead, use a Runnable or a Callable to represent units of work, and let the ExecutorService provide a suitable Future.

Alternatively, if you're willing to move slightly further away from your C++ model, and if you don't need the work to run on a specific thread, then you could consider skipping the ExecutorService and using SwingWorker.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • The code that needs to get run *needs* to run on the JavaFX thread. Hence my use of `Platform.runLater`. But for whatever reason, the `Platform` class doesn't have a `runAndWait` function in the same way that Swing has both `invokeLater` and `invokeAndWait`. – Xirema Mar 20 '17 at 16:15
  • @Xirema, I have completely rewritten this answer with an alternative method to achieve what you're after, including handling `RuntimeException`s. – John Bollinger Mar 20 '17 at 16:54
  • @JohnBollinger : this looks good... For dealing with exceptions and working in a future like manner, I'd have used CompletableFuture and complete/completeExceptionaly constructs... – GPI Mar 20 '17 at 17:00
  • This looks a lot closer to what I need. I'll try it out and see if it works for me. – Xirema Mar 20 '17 at 17:06
  • @GPI, certainly it could be done with a `CompletableFuture`, but IMO, any kind of `Future` is the wrong tool for this job, as I attempted to explain at the beginning of the answer. In any event, that doesn't solve the underlying problem of waiting until the other thread finishes the task at hand. Thus, you could wrap a `CompletableFuture` around the implementation I suggested or some similar one, but you don't that way gain much, if anything. – John Bollinger Mar 20 '17 at 17:58
  • Fair enough. The things that I like about Future, here, is the fact that it gets around the synchronous queue and implicit semantic that "null means normal completion". But of course it is debatable. – GPI Mar 21 '17 at 08:07
2

This question seems similar to:

And the runAndWait code from your question looks very similar to Sarcan's answer, which is this:

final FutureTask query = new FutureTask(new Callable() {
    @Override
    public Object call() throws Exception {
        return queryPassword();
    }
});
Platform.runLater(query);
System.out.println(query.get());

In comments on other answers I notice you are also concerned about Exception handling. You will notice that FutureTask has logic to setException():

Causes this future to report an ExecutionException with the given throwable as its cause, unless this future has already been set or has been cancelled. This method is invoked internally by the run() method upon failure of the computation.

As the internal implementation invokes the setException call, you don't need to explicitly invoke setException. Any uncaught exception thrown in the context of the FutureTask will be set in that FutureTask and you can interpret it via catching an ExecutionException from your future.get() call.

// non-JavaFX thread code...

Future<Void> future = new FutureTask<>(() -> {
        // work to be done on the JavaFX thread...
        return null;
    }
});

// do the work on the JavaFX thread.
Platform.runLater(future);

try {
    // await completion of the work on the JavaFX thread.
    future.get();
} catch (InterruptedException ex) {
    // restore the interrupt status (see the linked Goetz article).
    Thread.currentThread().interrupt();
} catch (ExecutionException ex) {
    // exception handling logic for an exception occurring 
    // in the body of the FutureTask here.
}

In the sample above I have a Future<Void> as I am not interested in passing any data result from the Future call. If I were interested in getting a result, then I could use Future<SomeObjectType> and have SomeObjectType result = future.get(). In situations like this I like to use immutable objects as much as possible (for example for SomeObjectType), though it is not strictly necessary for the future.get() scenario as that essentially is accessing objects in sequence rather than in parallel across threads.

If you wanted to re-throw the exception that occurred on the JavaFX application thread on the non-JavaFX thread, then you could do this:

} catch (ExecutionException ex) {
    throw ex.getCause();
}

The following information is for different thread based interactions with JavaFX (or Java in general) and does not directly relate to the question, so it can be ignored for the specifics of answering the question, it merely serves as background info.

Some background info: I find a really excellent on implementation of tasks in Java to be Brian Goetz's article:

Opposite interaction: The example given above relates to invoking tasks on the JavaFX application thread from another thread and awaiting their completion. If you have the opposite situation where you want to invoke a Task on another thread instead of the JavaFX thread, then you would use a JavaFX Task. In such a case you don't want the JavaFX thread to await completion of the non-JavaFX task as you should never pause or suspend the JavaFX thread (instead the Task call must be executed concurrently, how to do this is explained in the linked Task Javadoc).

There are mechanisms for interacting between the background thread and the JavaFX UI detailed in the related (but different) question:

Community
  • 1
  • 1
jewelsea
  • 150,031
  • 14
  • 366
  • 406
0

Here's a possible solution:

public static void runAndWait(Runnable runner) {
    Future<Object> future = new FutureTask<>(new Callable<Object>() {
        public Object call() {
            runner.run();
            return null;
        }
    });

    try {
        future.get(); // exception handling happens here.
    } catch (InterruptedException ex) {
        Logger.getLogger(MainApp.class.getName()).log(Level.SEVERE, null, ex);
    } catch (ExecutionException ex) {
        Logger.getLogger(MainApp.class.getName()).log(Level.SEVERE, null, ex);
    }
}

Callable and Runnable are separate interfaces, so there's no way to submit a Callable object to Platform.runLater(), unless you re-wrap the Callable inside another Runnable, but that would be nonsensical. Calling future.get() will cause the Future to be evaluated (while blocking execution, which may or may not be what you want). If you omit the try/catch inside the Callable you have to handle it when you call future.get(). However, if you don't need a result back (since you are returning null) you can simply pass the runnable straight away:

public static void runAndWait(Runnable runner) {
    try{
        Platform.runLater(runner);
    } catch (Exception ex) {
        // handle exceptions; generic Exception used for brevity
    }
}

However this version doesn't let you explicitly decide when you want the task to run; it will execute whenever Platform.runLater() decides to run it:

Run the specified Runnable on the JavaFX Application Thread at some unspecified time in the future. This method, which may be called from any thread, will post the Runnable to an event queue and then return immediately to the caller. The Runnables are executed in the order they are posted. A runnable passed into the runLater method will be executed before any Runnable passed into a subsequent call to runLater. If this method is called after the JavaFX runtime has been shutdown, the call will be ignored: the Runnable will not be executed and no exception will be thrown.

Intuitively, I would think that this means the first Runnable is executed almost immediately, and subsequent Runnables are executed as each previous one completes, but I don't know that firsthand.


After comments, here's another possibility:

public static void runAndWait(Runnable runner) throws Exception {
    ExecutorService exec = Executors.newCachedThreadPool();
    exec.submit(runner);
    exec.shutdown();
    exec.awaitTermination(3l, TimeUnit.SECONDS);
}

This will let you set a timeout for the waiting period, which can be arbitrarily short or long (3 seconds in this example). This will block execution until the Runnable thread completes, so if it's running in the FX thread, then there's no need to message that it's completed, you should be able to just continue execution beyond that point.

NAMS
  • 983
  • 7
  • 17
  • None of these solutions satisfy the constraints I'm working with. What I specifically need is for my `Runnable` to run on the JavaFX thread, and for *my current thread to wait until it has completed*. Hence my use of futures in the first place. – Xirema Mar 20 '17 at 16:13
  • @Xirema, what's the point of using a `Future` if you intend always to wait until the computation is complete? – John Bollinger Mar 20 '17 at 16:15
  • @JohnBollinger Because I need a mechanism that informs the original thread that the task is complete. – Xirema Mar 20 '17 at 16:16
  • @NAMS It's the same problem: Your last example doesn't put the `runner` on the JavaFX thread, it just puts it on a new Thread Pool. The code *needs* to run on the JavaFX thread, or it straight-up will not work, due to concurrency restrictions in JavaFX. – Xirema Mar 20 '17 at 16:33
  • If the thread pool is on the FX thread, it will be blocked while awaiting termination. But if you need the FX thread to wait, why not just run the code synchronously from the start? – NAMS Mar 20 '17 at 16:38