Philipp Haller and Stephen Tu
This guide describes the API of the scala.actors package of Scala 2.8/2.9. The organization follows groups of types that logically belong together. The trait hierarchy is taken into account to structure the individual sections. The focus is on the run-time behavior of the various methods that these traits define, thereby complementing the existing Scaladoc-based API documentation.
NOTE: In Scala 2.10 the Actors library is deprecated and will be removed in future Scala releases. Users should use Akka actors from the package akka.actor. For migration from Scala actors to Akka refer to the Actors Migration Guide.
Reactor is the super trait of all actor traits. Extending this trait allows defining actors with basic capabilities to send and receive messages.
The behavior of a Reactor is defined by implementing its act method. The act method is executed once the Reactor is started by invoking start, which also returns the Reactor. The start method is idempotent which means that invoking it on an actor that has already been started has no effect.
The Reactor trait has a type parameter Msg which indicates the type of messages that the actor can receive.
Invoking the Reactor's ! method sends a message to the receiver. Sending a message using ! is asynchronous which means that the sending actor does not wait until the message is received; its execution continues immediately. For example, a ! msg sends msg to a. All actors have a mailbox which buffers incoming messages until they are processed.
The Reactor trait also defines a forward method. This method is inherited from OutputChannel. It has the same effect as the ! method. Subtraits of Reactor, in particular the ReplyReactor trait, override this method to enable implicit reply destinations (see below).
A Reactor receives messages using the react method. react expects an argument of type PartialFunction[Msg, Unit] which defines how messages of type Msg are handled once they arrive in the actor's mailbox. In the following example, the current actor waits to receive the string "Hello", and then prints a greeting:
react {
case "Hello" => println("Hi there")
}
Invoking react never returns. Therefore, any code that should run after a message has been received must be contained inside the partial function that is passed to react. For example, two messages can be received in sequence by nesting two invocations of react:
react {
case Get(from) =>
react {
case Put(x) => from ! x
}
}
The Reactor trait also provides control structures which simplify programming with react.
The execution of a Reactor terminates when the body of its act method has run to completion. A Reactor can also terminate itself explicitly using the exit method. The return type of exit is Nothing, because exit always throws an exception. This exception is only used internally, and should never be caught.
A terminated Reactor can be restarted by invoking its restart method. Invoking restart on a Reactor that has not terminated, yet, throws an IllegalStateException. Restarting a terminated actor causes its act method to be rerun.
Reactor defines a method getState which returns the actor's current execution state as a member of the Actor.State enumeration. An actor that has not been started, yet, is in state Actor.State.New. An actor that can run without waiting for a message is in state Actor.State.Runnable. An actor that is suspended, waiting for a message is in state Actor.State.Suspended. A terminated actor is in state Actor.State.Terminated.
The exceptionHandler member allows defining an exception handler that is enabled throughout the entire lifetime of a Reactor:
def exceptionHandler: PartialFunction[Exception, Unit]
exceptionHandler returns a partial function which is used to handle exceptions that are not otherwise handled: whenever an exception propagates out of the body of a Reactor's act method, the partial function is applied to that exception, allowing the actor to run clean-up code before it terminates. Note that the visibility of exceptionHandler is protected.
Handling exceptions using exceptionHandler works well together with the control structures for programming with react. Whenever an exception has been handled using the partial function returned by exceptionHandler, execution continues with the current continuation closure. Example:
loop {
react {
case Msg(data) =>
if (cond) // process data
else throw new Exception("cannot process data")
}
}
Assuming that the Reactor overrides exceptionHandler, after an exception thrown inside the body of react is handled, execution continues with the next loop iteration.
The ReplyReactor trait extends Reactor[Any] and adds or overrides the following methods:
The ! method is overridden to obtain a reference to the current
actor (the sender); together with the actual message, the sender
reference is transferred to the mailbox of the receiving actor. The
receiver has access to the sender of a message through its sender
method (see below).
The forward method is overridden to obtain a reference to the
sender of the message that is currently being processed. Together
with the actual message, this reference is transferred as the sender
of the current message. As a consequence, forward allows
forwarding messages on behalf of actors different from the current
actor.
The added sender method returns the sender of the message that is
currently being processed. Given the fact that a message might have
been forwarded, sender may not return the actor that actually sent
the message.
The added reply method sends a message back to the sender of the
last message. reply is also used to reply to a synchronous message
send or a message send with future (see below).
The added !? methods provide synchronous message sends. Invoking
!? causes the sending actor to wait until a response is received
which is then returned. There are two overloaded variants. The
two-parameter variant takes in addition a timeout argument (in
milliseconds), and its return type is Option[Any] instead of
Any. If the sender does not receive a response within the
specified timeout period, !? returns None, otherwise it returns
the response wrapped in Some.
The added !! methods are similar to synchronous message sends in
that they allow transferring a response from the receiver. However,
instead of blocking the sending actor until a response is received,
they return Future instances. A Future can be used to retrieve
the response of the receiver once it is available; it can also be
used to find out whether the response is already available without
blocking the sender. There are two overloaded variants. The
two-parameter variant takes in addition an argument of type
PartialFunction[Any, A]. This partial function is used for
post-processing the receiver's response. Essentially, !! returns a
future which applies the partial function to the response once it is
received. The result of the future is the result of this
post-processing.
The added reactWithin method allows receiving messages within a
given period of time. Compared to react it takes an additional
parameter msec which indicates the time period in milliseconds
until the special TIMEOUT pattern matches (TIMEOUT is a case
object in package scala.actors). Example:
reactWithin(2000) { case Answer(text) => // process text case TIMEOUT => println("no answer within 2 seconds") }
The reactWithin method also allows non-blocking access to the
mailbox. When specifying a time period of 0 milliseconds, the
mailbox is first scanned to find a matching message. If there is no
matching message after the first scan, the TIMEOUT pattern
matches. For example, this enables receiving certain messages with a
higher priority than others:
reactWithin(0) { case HighPriorityMsg => // ... case TIMEOUT => react { case LowPriorityMsg => // ... } }
In the above example, the actor first processes the next
HighPriorityMsg, even if there is a LowPriorityMsg that arrived
earlier in its mailbox. The actor only processes a LowPriorityMsg
first if there is no HighPriorityMsg in its mailbox.
In addition, ReplyReactor adds the Actor.State.TimedSuspended execution state. A suspended actor, waiting to receive a message using reactWithin is in state Actor.State.TimedSuspended.
The Actor trait extends ReplyReactor and adds or overrides the following members:
The added receive method behaves like react except that it may
return a result. This is reflected in its type, which is polymorphic
in its result: def receive[R](f: PartialFunction[Any, R]): R.
However, using receive makes the actor more heavyweight, since
receive blocks the underlying thread while the actor is suspended
waiting for a message. The blocked thread is unavailable to execute
other actors until the invocation of receive returns.
The added link and unlink methods allow an actor to link and unlink
itself to and from another actor, respectively. Linking can be used
for monitoring and reacting to the termination of another actor. In
particular, linking affects the behavior of invoking exit as
explained in the API documentation of the Actor trait.
The trapExit member allows reacting to the termination of linked
actors independently of the exit reason (that is, it does not matter
whether the exit reason is 'normal or not). If an actor's trapExit
member is set to true, this actor will never terminate because of
linked actors. Instead, whenever one of its linked actors terminates
it will receive a message of type Exit. The Exit case class has two
members: from refers to the actor that terminated; reason refers to
the exit reason.
When terminating the execution of an actor, the exit reason can be set
explicitly by invoking the following variant of exit:
def exit(reason: AnyRef): Nothing
An actor that terminates with an exit reason different from the symbol
'normal propagates its exit reason to all actors linked to it. If an
actor terminates because of an uncaught exception, its exit reason is
an instance of the UncaughtException case class.
The Actor trait adds two new execution states. An actor waiting to
receive a message using receive is in state
Actor.State.Blocked. An actor waiting to receive a message using
receiveWithin is in state Actor.State.TimedBlocked.
The Reactor trait defines control structures that simplify programming
with the non-returning react operation. Normally, an invocation of
react does not return. If the actor should execute code subsequently,
then one can either pass the actor's continuation code explicitly to
react, or one can use one of the following control structures which
hide these continuations.
The most basic control structure is andThen. It allows registering a
closure that is executed once the actor has finished executing
everything else.
actor {
{
react {
case "hello" => // processing "hello"
}: Unit
} andThen {
println("hi there")
}
}
For example, the above actor prints a greeting after it has processed
the "hello" message. Even though the invocation of react does not
return, we can use andThen to register the code which prints the
greeting as the actor's continuation.
Note that there is a type ascription that follows the react
invocation (: Unit). Basically, it let's you treat the result of
react as having type Unit, which is legal, since the result of an
expression can always be dropped. This is necessary to do here, since
andThen cannot be a member of type Nothing which is the result
type of react. Treating the result type of react as Unit allows
the application of an implicit conversion which makes the andThen
member available.
The API provides a few more control structures:
loop { ... }. Loops indefinitely, executing the code in braces in
each iteration. Invoking react inside the loop body causes the
actor to react to a message as usual. Subsequently, execution
continues with the next iteration of the same loop.
loopWhile (c) { ... }. Executes the code in braces while the
condition c returns true. Invoking react in the loop body has
the same effect as in the case of loop.
continue. Continues with the execution of the current continuation
closure. Invoking continue inside the body of a loop or
loopWhile will cause the actor to finish the current iteration and
continue with the next iteration. If the current continuation has
been registered using andThen, execution continues with the
closure passed as the second argument to andThen.
The control structures can be used anywhere in the body of a Reactor's
act method and in the bodies of methods (transitively) called by
act. For actors created using the actor { ... } shorthand the control
structures can be imported from the Actor object.
The ReplyReactor and Actor traits support result-bearing message
send operations (the !! methods) that immediately return a
future. A future, that is, an instance of the Future trait, is a
handle that can be used to retrieve the response to such a message
send-with-future.
The sender of a message send-with-future can wait for the future's
response by applying the future. For example, sending a message using
val fut = a !! msg allows the sender to wait for the result of the
future as follows: val res = fut().
In addition, a Future can be queried to find out whether its result
is available without blocking using the isSet method.
A message send-with-future is not the only way to obtain a
future. Futures can also be created from computations using the future
method. In the following example, the computation body is started to
run concurrently, returning a future for its result:
val fut = future { body }
// ...
fut() // wait for future
What makes futures special in the context of actors is the possibility
to retrieve their result using the standard actor-based receive
operations, such as receive etc. Moreover, it is possible to use the
event-based operations react and reactWithin. This enables an actor to
wait for the result of a future without blocking its underlying
thread.
The actor-based receive operations are made available through the
future's inputChannel. For a future of type Future[T], its type is
InputChannel[T]. Example:
val fut = a !! msg
// ...
fut.inputChannel.react {
case Response => // ...
}
Channels can be used to simplify the handling of messages that have
different types but that are sent to the same actor. The hierarchy of
channels is divided into OutputChannels and InputChannels.
OutputChannels can be sent messages. An OutputChannel out
supports the following operations.
out ! msg. Asynchronously sends msg to out. A reference to the
sending actor is transferred as in the case where msg is sent
directly to an actor.
out forward msg. Asynchronously forwards msg to out. The
sending actor is determined as in the case where msg is forwarded
directly to an actor.
out.receiver. Returns the unique actor that is receiving messages
sent to the out channel.
out.send(msg, from). Asynchronously sends msg to out supplying
from as the sender of the message.
Note that the OutputChannel trait has a type parameter that specifies
the type of messages that can be sent to the channel (using !,
forward, and send). The type parameter is contravariant:
trait OutputChannel[-Msg]
Actors can receive messages from InputChannels. Like OutputChannel,
the InputChannel trait has a type parameter that specifies the type of
messages that can be received from the channel. The type parameter is
covariant:
trait InputChannel[+Msg]
An InputChannel[Msg] in supports the following operations.
in.receive { case Pat1 => ... ; case Patn => ... } (and similarly,
in.receiveWithin). Receives a message from in. Invoking
receive on an input channel has the same semantics as the standard
receive operation for actors. The only difference is that the
partial function passed as an argument has type
PartialFunction[Msg, R] where R is the return type of receive.
in.react { case Pat1 => ... ; case Patn => ... } (and similarly,
in.reactWithin). Receives a message from in using the
event-based react operation. Like react for actors, the return
type is Nothing, indicating that invocations of this method never
return. Like the receive operation above, the partial function
passed as an argument has a more specific type:
PartialFunction[Msg, Unit]
Channels are created using the concrete Channel class. It extends both
InputChannel and OutputChannel. A channel can be shared either by
making the channel visible in the scopes of multiple actors, or by
sending it in a message.
The following example demonstrates scope-based sharing.
actor {
var out: OutputChannel[String] = null
val child = actor {
react {
case "go" => out ! "hello"
}
}
val channel = new Channel[String]
out = channel
child ! "go"
channel.receive {
case msg => println(msg.length)
}
}
Running this example prints the string "5" to the console. Note that
the child actor has only access to out which is an
OutputChannel[String]. The channel reference, which can also be used
to receive messages, is hidden. However, care must be taken to ensure
the output channel is initialized to a concrete channel before the
child sends messages to it. This is done using the "go" message. When
receiving from channel using channel.receive we can make use of the
fact that msg is of type String; therefore, it provides a length
member.
An alternative way to share channels is by sending them in messages. The following example demonstrates this.
case class ReplyTo(out: OutputChannel[String])
val child = actor {
react {
case ReplyTo(out) => out ! "hello"
}
}
actor {
val channel = new Channel[String]
child ! ReplyTo(channel)
channel.receive {
case msg => println(msg.length)
}
}
The ReplyTo case class is a message type that we use to distribute a
reference to an OutputChannel[String]. When the child actor receives a
ReplyTo message it sends a string to its output channel. The second
actor receives a message on that channel as before.
A Reactor (or an instance of a subtype) is executed using a
scheduler. The Reactor trait introduces the scheduler member which
returns the scheduler used to execute its instances:
def scheduler: IScheduler
The run-time system executes actors by submitting tasks to the
scheduler using one of the execute methods defined in the IScheduler
trait. Most of the trait's other methods are only relevant when
implementing a new scheduler from scratch, which is rarely necessary.
The default schedulers used to execute instances of Reactor and Actor
detect the situation when all actors have finished their
execution. When this happens, the scheduler shuts itself down
(terminating any threads used by the scheduler). However, some
schedulers, such as the SingleThreadedScheduler (in package scheduler)
have to be shut down explicitly by invoking their shutdown method.
The easiest way to create a custom scheduler is by extending
SchedulerAdapter, implementing the following abstract member:
def execute(fun: => Unit): Unit
Typically, a concrete implementation would use a thread pool to
execute its by-name argument fun.
This section describes the remote actors API. Its main interface is
the RemoteActor object in package scala.actors.remote. This object
provides methods to create and connect to remote actor instances. In
the code snippets shown below we assume that all members of
RemoteActor have been imported; the full list of imports that we use
is as follows:
import scala.actors._
import scala.actors.Actor._
import scala.actors.remote._
import scala.actors.remote.RemoteActor._
A remote actor is uniquely identified by a Symbol. This symbol is
unique to the JVM instance on which the remote actor is executed. A
remote actor identified with name 'myActor can be created as follows.
class MyActor extends Actor {
def act() {
alive(9000)
register('myActor, self)
// ...
}
}
Note that a name can only be registered with a single (alive) actor at
a time. For example, to register an actor A as 'myActor, and then
register another actor B as 'myActor, one would first have to wait
until A terminated. This requirement applies across all ports, so
simply registering B on a different port as A is not sufficient.
Connecting to a remote actor is just as simple. To obtain a remote
reference to a remote actor running on machine myMachine, on port
8000, with name 'anActor, use select in the following manner:
val myRemoteActor = select(Node("myMachine", 8000), 'anActor)
The actor returned from select has type AbstractActor which provides
essentially the same interface as a regular actor, and thus supports
the usual message send operations:
myRemoteActor ! "Hello!"
receive {
case response => println("Response: " + response)
}
myRemoteActor !? "What is the meaning of life?" match {
case 42 => println("Success")
case oops => println("Failed: " + oops)
}
val future = myRemoteActor !! "What is the last digit of PI?"
Note that select is lazy; it does not actually initiate any network
connections. It simply creates a new AbstractActor instance which is
ready to initiate a new network connection when needed (for instance,
when ! is invoked).
Contents