Scala Actors: Unifying thread-based and event-based programming

Scala Actors: Unifying thread-based and event-based programming – Haller & Odersky 2008

Yesterday we saw a Haskell-based approach to unifying events and threads, today’s paper shows how to apply some of those same ideas on top of the JVM using Scala.

There is an impedance mismatch between message-passing concurrency and virtual machines, such as the JVM. VMs usually map their threads to heavyweight OS processes. Without a lightweight process abstraction, users are often forced to write parts of concurrent applications in an event-driven style which obscures control flow, and increases the burden on the programmer. In this paper we show how thread-based and event-based programming can be unified under a single actor abstraction. Using advanced abstraction mechanisms of the Scala programming language, we implement our approach on unmodified JVMs.

What is an actor?

An actor is a concurrent process that communicates with other actors by exchanging messages. Communication is asynchronous; messages are buffered in an actor’s mailbox. An actor may respond to an asynchronous message by creating new actors, sending messages to known actors (including itself), or changing its behavior. The behavior specifies how the actor responds to the next message that it receives.

Some of the authors we read earlier in the week would probably put actors in the ‘event-based’ category. As Haller and Odersky point out though, they do avoid the inversion of control issue with out and out event-driven systems. They call it ‘message-based’ concurrency – the same term used in the original ‘dual’ paper we looked at on Monday.

An actor can suspend with a full thread stack (receive) or it can suspend with just a continuation closure (react). The first form of suspension corresponds to thread-based, the second form to event-based programming. The new system combines the benefits of both models…

Complexity is reduced for the programmer since:

  • Accessing an actor’s mailbox is race-free by design
  • Message-passing with pattern matching is often more convenient than explicit thread-based synchronization
  • Actors are lightweight, supported fine-grained concurrency models without the need for explicit management of thread pools
  • Actors interoperate naturally with normal JVM threads (that are treated as actors).

The scheme is implemented in the Scala Actors library.

… it might seem that Scala is a language specialized for actor concurrency. In fact, this is not true. Scala only assumes the basic thread model of the underlying host.

The implementation executes multiple actors on multiple threads:

The basic idea of our implementation is to use a thread pool to execute actors, and to resize the thread pool whenever it is necessary to support general thread operations. If actors use only operations of the event-based model, the size of the thread pool can be fixed. This is different if some of the actors use blocking operations such as receive or system I/O. In the case where every worker thread is occupied by a blocked actor and there are pending tasks, the thread pool has to grow.

Combinators are introduced to enable actor composition. andThen for example permits sequential composition in a manner reminiscent of the CPS Monad of Li and Zdancewic.

[code lang=text]
awaitPing andThen sendPong

There are several examples of actors given in the paper. But before you dive in too deep, it’s worth knowing that the Scala Actors library was deprecated in Scala 2.11 in favour of Akka. See the Akka documentation on Actors for an overview of the currently supported actor model. The basic actor notions apply equally in both of course.

Section 6 of the paper gives a short discussion of the benefits of the actor model when building web applications.

Compared to a purely event-based approach, users are relieved from writing their own ad hoc thread pooling code. Since the internal thread pool can be global to the web application server, the thread pool controller can leverage more information for its decisions. Finally, accesses to an actor’s mailbox are race-free. Therefore, resources such as user profiles can be protected by modeling them as (thread-less) actors.

It seems fitting to end with a tribute to Erlang:

Our library was inspired to a large extent by Erlang’s elegant programming model. Erlang is a dynamically-typed functional programming language designed for programming real-time control systems. The combination of lightweight isolated processes, asynchronous message passing with pattern matching, and controlled error propagation has been proven to be very effective. One of our main contributions lies in the integration of Erlang’s programming model into a full-fledged object-oriented and functional language.