Sunday, May 31, 2015

The Triple-Blocking problem in Actor Models

Actor Models are a nice new feature in many languages, either through direct support in the language like Clojure, or in a library like Akka for Java and Scala.
Actors are an interesting tool to deal with concurrency, that can be extremely successful at resolving certain kinds of problems, but they are not the "silver bullet".
If you want to see some of its flaws, then take a look at this presentation from Akka Days by Derek Wyatt from Auvik Networks... you can skip to minute 14 which is when the interesting stuff starts, and goes up to minute 50:

Besides the issues Derek points out, there is one more which I call "The Triple-Blocking Problem". Contrary to what many people think, just because it is asynchronous, it doesn't mean it is non-blocking, and in fact, most Actor Models are blocking in three different ways:
  1. The message passing queue is "blocking";
  2. An Actor can block waiting for another Actor;
  3. An actor will block waiting for a Future to have its result ready;

Let's look at the first one.
Several actor implementations use a blocking queue for the message passing mechanism, which means that only one thread/actor at a time can send messages to a given actor.
Perhaps even more worrisome, is that some implementations use a queue by Dmitry Yuokov that is wait-free for enqueueing and blocking for dequeueing, which is non-blocking for the act of enqueueing, but an actor dequeueing messages from its message queue can block indefinitely waiting for an enqueuer to complete, i.e. link the next node to the list.

The second case happens when an actor wants the result of a computation from another actor immediately. The other actor must first process the previous messages in the queue, and then execute the corresponding work/computation, and then provide the result. At any instant, the thread running the actor executing the work can be delayed/preempted/sleep/die, whatever.
The simple act of delegating the work to another actor/thread means that it can inherently block.

The third case is a kind of extension of the second case.
An actor can tell another actor to do some work/computation and put the result in a Future, but regardless of what is done by the first actor, it will need the result, at some point in time. When that happens, if the result is not yet ready, the first actor will block. It's as simple as that.

Having said all of this, remember that, "just because it's blocking, doesn't mean it's bad"!
Even blocking methods/actors can scale well under the right circumstances, so there is hope for the Actor Model, but don't expect actors to solve all your concurrency problems.

The actor model is at its essence a message passing mechanism with a Multi-Producer-Single-Consumer-wait-free-for-producers-blocking-for-consumers-queue (at best), and saying that we want to solve all problems in concurrency with a single type of queue is just as naive as saying we want to solve all problems in concurrency with a single type of lock. To attack concurrent problems we will always need multiple techniques: Locks, RW-Locks, Left-Right, Copy-On-Write, Lock-Free data structures, Wait-Free data structures, etc...

1 comment:

  1. Indeed it's a problem, but this is a problem in the concrete Actor implementation. Akka use that model, but in Quasar the implementation is better from that perspective, in any blocking call that you mention, the active Actor yield to a ready-for-running Actor so the native thread doesn't waste time on blocking.