JavaFx2 or ScalaFx + Akka
Asked Answered
F

3

21

How to run Akka actors in a JavaFX/ScalaFX Application ?

(This is an update of question based on the first answers)

Is the solution to share the same execution context? Meaning having the Actors dispatchers based on the JavaFx ExecutorService ? (The one with which on which it runs UI manipulation code)

Does that mean one agent would represent the UI and be able to manipulate it ? I mean because suggested below if a couple of actor are on the UI ExecutorService, doesn't that mean share a state between agent (the object being the UI)?

Can 2 actors communicate while being on different executor services? I'm asking this because from what is suggested below, some agent would be on the UI Executor Service while other not.

Finally, why using akka as is, with its on Executor context different and using Platform.runLater, might have some consequence on the performance of the UI. I this pose the question of multiple executor service on the same application: Is that bad?

Faris answered 29/12, 2013 at 18:41 Comment(3)
Yes, it is possible. I've written some three or four applications using Scala, Akka and ScalaFX for internal use. Writing JavaFX/ScalaFX multithreaded applications does not differ much from writing any other multithreaded applications. Do you have any specific problems which caused you to ask this question in the first place? I'm asking this because in the present form your question, IMO, is not suitable for Stackoverflow.Bellman
If you are interested at an example application using scala, akka and scalafx you could check out Scalatrix. It's a simple tetris game I created to test scala, akka and scalafx.Crosspollination
Can 2 actors communicate while being on different executor services? Yes, look at the Controller actor from Scalatrix. It spawns the model actor on the same execution context and the view actor on the JavaFx Execution context. They can all communicate with each other.Crosspollination
B
22
  • Futures

The best way to use scala Futures together with a single-threaded toolkit such as JavaFX would be to define an executor that allows you to execute futures or actors on the thread of the UI Toolkit.

The same problem exists for Swing, which also requires updates to happen on the swing thread. Viktor Klang came up with the following solution Swing Execution Context. Here it is translated for JavaFX:

import akka.dispatch.ExecutionContext
import javafx.application.Platform
import java.util.concurrent.Executor

//
object JavaFXExecutionContext {
  implicit val javaFxExecutionContext: ExecutionContext = ExecutionContext.fromExecutor(new Executor {
  def execute(command: Runnable): Unit = Platform.runLater(command)
  })
}

You would use it like this:

// import the default ec
import scala.concurrent.ExecutionContext.Implicits.global
// define the JavaFX ec so we can use it explicitly
val fxec = JavaFXExecutionContext.javaFxExecutionContext
future {
  // some asynchronous computation, running on the default
  // ForkJoin ExecutionContext because no ec is passed
  // explicitly
}.map(result => {
  // update JavaFX components from result
  // This will run in the JavaFX thread.
  // Check Platform.isFxApplicationThread() to be sure!
})(fxec)

The pipeline of futures can be very complex, as long as the step(s) that interact with JavaFX components are all running on the JavaFX ExecutionContext.

Note: it is up to you whether you make the ForkJoin ec the default and pass the JavaFX ec explicitly or vice versa. It might be a good idea to make the JavaFX ec the default to prevent errors and mark the parts that can run asynchronously explicitly with the ForkJoin ec.

  • Actors

For integrating an Actor based system with a single-threaded UI toolkit there is also a solution. See Swing Actors. All you have to do is to replace the

SwingUtilities.invokeLater(command)

with

Platform.runLater(command)

and you're good to go!

  • When to use which

If you have a big UI application and just want to spin off some asynchronous operations (loading a file or doing some computation), the futures-based approach is probably preferable. But be careful not to do any interaction (neither read nor write) with JavaFX components in the futures that run asynchronously on the default execution context.

If you have a large actor based system and just want to attach an UI to some parts, having a few actors that run on the JavaFX thread is probably preferable. YMMV.

Billybillycock answered 30/12, 2013 at 9:44 Comment(4)
I'm not sure to fully understand the Actor Solution. Could you explain what is the fundamental problem of having the executionContext of your AKKA actor different from the one of JAVAFX ? Or shall I say the executorServices? What is the main goal of the propose solution? When would you need to have actors on the same executorService as JavaFX UI Thread ?Faris
If you run an actor that has the default execution context configured, it gets executed on a thread-pool thread. But modifying and even accessing the state of a JavaFX GUI component from an arbitrary thread is not allowed and will result in undefined behavior. So the trick is to configure all actors that interact with the GUI in some way to execute on the GUI thread using the JavaFXExecutionContext. That way you have an actor that behaves just like any other actor (you can safely pass its ActorRef around) but can safely access the GUI state from inside its receive method.Graeco
Would using Platform.runlater from within the agent for updating the UI not suffice ? despite the fact that the actors system as his own Executioncontext/Execution service/ThreadPool ?Faris
I thought that the original problem was having 2 threadPool (with one being extremely heavy) in the same application being the problem. But actually the objective here was to update the UI by "making platform-runLater invisible to the programmer" and therefore fully using a completely different threading model. That is, communicating with the UI via message passing rather than method.Faris
I
6

There are a couple of things you need to consider when using multiple threads in JavaFX:

  • Any code that ends up touching the scene graph (e.g. by updating data that is bound to controls) must be wrapped in Platform.runLater. If you use the built-in multi-threading API in JavaFX (i.e. Task and Service) this will be done automatically, but if you use any other multi-threading utility you must do it yourself.

  • Your multi-threading utility (i.e. Akka) must (I think) somehow be told to leave some room for the JavaFX event thread. If you look at the source for Service you see that they take quite some care when configuring the executor. I'm not sure about the details of this, but when I experimented with using Scala's Futures with JavaFX, I observed some unresponsiveness in the UI when using the default ExecutionContext, that disappeared when I used a custom one based on the Service implementation.

There is no support in ScalaFX (or any other toolkit as far as I know) for working with either Scala Futures or Akka in a way that lets you forget about the two points above, but it would surely be interesting to look into.

Inveigh answered 29/12, 2013 at 21:53 Comment(1)
Your multi-threading utility (i.e. Akka) must (I think) somehow be told to leave some room for the JavaFX event thread. >>> Could further explain that?Faris
C
6

Here is the gist about akka actors that can be run on the Swing or JavaFX thread. It is a convenient, copy-pastable extension of Victor Klang's Swing Actors based on Rüdiger's answer.

Crosspollination answered 13/2, 2014 at 10:52 Comment(11)
see my comment in your github. Memory leak.Marksman
Did you mean that may use the same executor context as JavaFx ?Faris
I don't understand the details, but in essence the actor's code runs in the JavaFx application thread. Refer to the Platform.runLater(Runnable) documentation.Crosspollination
The problem is if you do modifications to the Swing/JavaFx GUI directly from another thread you will get concurrent modification exceptions. That's why you can use SwingUtilities.invokeLater(Runnable) and Platform.runLater(Runnable) to solve the thread concurrency issue. Look SO answer.Crosspollination
Yes i understand that. I was just wondering, what's wrong with calling Platform.runLater from within an actor? Meaning explicitly. This would mean some how that from a thread within a threadpool, you ask some code to be executed in another thread from another thread pool.Faris
So i understand very well what you are doing. I just don't understand, why you couldn't simply, create your actor system with its own execution context and then, from within the actor, call platform run later when you need to execute some code that manipulate the GUI? If you tell me, the idea is too completely replace the threading model, that is, only coding with actor and future and hiding away the Platform.runLater details, then i would understand. But i'm wondering if that is the concern here or if there is something else.Faris
What's wrong with calling Platform.runLater from within an actor? There is nothing wrong with that. As you said it is hiding away the Platform.runLater details, thus I don't have to wrap my actor code inside of Platform.runLater every time I make a call to the GUI.Crosspollination
However, if you run the actor on the javafx thread, all of the actor code is executed on the GUI thread and may slow down GUI responsiveness if you do hefty computations there.Crosspollination
I am trying to use that, but I get an error because the toolkit is not initialized. What is missing? Do I have to put a new JFXPanel() somewhere, create an JFX Application object or something like that?Mockup
@Mockup Yep, new JFXPanel() seems like a reasonable workaround.Crosspollination
@Crosspollination I finally understood how to build the complete application by reading the scalatrix code... Thanks!Mockup

© 2022 - 2024 — McMap. All rights reserved.