Event-Based Programming Without Inversion of Control
Event-Based Programming Without Inversion of Control
Control
1 Introduction
Concurrent programming is indispensable. On the one hand, distributed and
mobile environments naturally involve concurrency. On the other hand, there
is a general trend towards multi-core processors that are capable of running
multiple threads in parallel.
With actors there exists a computation model which is especially suited for con-
current and distributed computations [16,1]. Actors are basically concurrent pro-
cesses which communicate through asynchronous message passing. When com-
bined with pattern matching for messages, actor-based process models have been
proven to be very e ective, as the success of Erlang documents [3,25].
Erlang [4] is a dynamically typed functional programming language designed
for programming real-time control systems. Examples of such systems are tele-
phone exchanges, network simulators and distributed resource controllers. In
these systems, large numbers of concurrent processes can be active simultane-
ously. Moreover, it is generally dicult to predict the number of processes and
their memory requirements as they vary with time.
For the implementation of these processes, operating system threads and threads
of virtual machines, such as the Java Virtual Machine [22], are usually too heavy-
weight. The main reasons are: (1) Over-provisioning of stacks leads to quick ex-
haustion of virtual address space and (2) locking mechanisms often lack suitable
contention managers [12]. Therefore, Erlang implements concurrent processes by
its own runtime system and not by the underlying operating system [5].
Actor abstractions as lightweight as Erlang's processes have been unavailable on
popular virtual machines so far. At the same time, standard virtual machines
are becoming an increasingly important platform for exactly the same domain of
applications in which Erlang{because of its process model{has been so successful:
Real-time control systems [23,27].
Another domain where virtual machines are expected to become ubiquitous are
applications running on mobile devices, such as cellular phones or personal digital
assistants [20]. Usually, these devices are exposed to severe resource constraints.
On such devices, only a few hundred kilobytes of memory is available to a virtual
machine and applications.
This has important consequences: (1) A virtual machine for mobile devices usu-
ally o ers only a restricted subset of the services of a common virtual machine
for desktop or server computers. For example, the KVM1 has no support for re-
ection (introspection) and serialization. (2) Programming abstractions used by
applications have to be very lightweight to be useful. Again, thread-based con-
currency abstractions are too heavyweight. Furthermore, programming models
have to cope with the restricted set of services a mobile virtual machine provides.
A common alternative to programming with threads are event-driven program-
ming models. Programming in explicitly event-driven models is very dicult [21].
Most programming models support event-driven programming only through in-
version of control. Instead of calling blocking operations (e.g. for obtaining user
input), a program merely registers its interest to be resumed on certain events
(e.g. an event signaling a pressed button, or changed contents of a text eld).
In the process, event handlers are installed in the execution environment which
are called when certain events occur. The program never calls these event han-
dlers itself. Instead, the execution environment dispatches events to the installed
handlers. Thus, control over the execution of program logic is \inverted".
Virtually all approaches based on inversion of control su er from the following
two problems: First, the interactive logic of a program is fragmented across
multiple event handlers (or classes, as in the state design pattern [13]). Second,
control ow among handlers is expressed implicitly through manipulation of
shared state [10].
To obtain very lightweight abstractions without inversion of control, we make
actors thread-less. We introduce event-based actors as an implementation tech-
nique for lightweight actor abstractions on non-cooperative virtual machines such
as the JVM. Non-cooperative means that the virtual machine provides no means
to explicitly manage the execution state of a program.
The central idea is as follows: An actor that waits in a receive statement is not
represented by a blocked thread but by a closure that captures the rest of the
actor's computation. The closure is executed once a message is sent to the actor
that matches one of the message patterns speci ed in the receive. The execution
of the closure is \piggy-backed" on the thread of the sender. If the receiving
closure terminates, control is returned to the sender as if a procedure returns. If
the receiving closure blocks in a second receive, control is returned to the sender
by throwing a special exception that unwinds the receiver's call stack.
A necessary condition for the scheme to work is that receivers never return
normally to their enclosing actor. In other words, no code in an actor can depend
on the termination or the result of a receive block. We can express this non-
returning property at compile time through Scala's type system. This is not a
severe restriction in practice, as programs can always be organized in a way so
that the \rest of the computation" of an actor is executed from within a receive.
1
See http://java.sun.com/products/cldc/.
To the best of our knowledge, event-based actors are the rst to (1) allow reactive
behavior to be expressed without inversion of control, and (2) support arbitrary
blocking operations in reactions, at the same time. Our actor library outper-
forms other state-of-the-art actor languages with respect to message passing
speed and memory consumption by several orders of magnitude. Our implemen-
tation is able to make use of multi-processors and multi-core processors because
reactions can be executed simultaneously on multiple processors. By extending
our event-based actors with a portable runtime system, we show how the essence
of distributed Erlang [31] can be implemented in Scala. Our library supports vir-
tually all primitives and built-in-functions which are introduced in the Erlang
book [4]. The portability of our runtime system is established by two working
prototypes based on TCP and the JXTA2 peer-to-peer framework, respectively.
All this has been achieved without extending or changing the programming
language. The event-based actor library is thus a good demonstrator of Scala's
abstraction capabilities. Beginning with the upcoming release 2.1.7, it is part of
the Scala standard distribution3 .
Other Related Work. Actalk [8] implements actors as a library for Smalltalk-80
by extending a minimal kernel of pure Smalltalk objects. Their implementa-
tion is not event-based and Smalltalk-80 does not support parallel execution of
concurrent actors on multi-processors (or multi-core processors).
Actra [29] extends the Smalltalk/V virtual machine with an object-based real-
time kernel which provides lightweight processes. In contrast, we implement
lightweight actors on unmodi ed virtual machines.
Chrysanthakopoulos and Singh [11] discuss the design and implementation of a
channel-based asynchronous messaging library. Channels can be viewed as spe-
cial state-less actors which have to be instantiated to indicate the types of mes-
sages they can receive. Instead of using heavyweight operating system threads
they develop their own scheduler to support continuation passing style (CPS)
code. Using CLU-style iterators blocking-style code is CPS-transformed by the
C# compiler.
SALSA (Simple Actor Language, System and Architecture) [30] extends Java
with concurrency constructs that directly support the notion of actors. A pre-
processor translates SALSA programs into Java source code which in turn is
linked to a custom-built actor library. As SALSA implements actors on the
JVM, it is somewhat closer related to our work than Smalltalk-based actors or
channels. Moreover, performance results have been published which enables us
to compare our system with SALSA, using ports of existing benchmarks.
Timber is an object-oriented and functional programming language designed for
real-time embedded systems [6]. It o ers message passing primitives for both
synchronous and asynchronous communication between concurrent reactive ob-
2
See http://www.jxta.org/.
3
Available from http://scala.ep .ch/.
class Counter extends Actor {
override def run(): unit = loop(0)
The rest of this paper is structured as follows. Section 2 shows how conventional,
thread-based actors are represented as a Scala library. Section 3 shows how to
modify the actor model so that it becomes event-based. Section 4 outlines Scala's
package for distributed actors. Section 5 evaluates the performance of our actor
libraries. Section 6 concludes.
2 Decomposing Actors
Messages that do not match the patterns Incr(), Value(a), or Lock(a) are
ignored. A typical communication with a counter actor could proceed as follows.
val counter = new Counter
counter.start()
counter ! Incr()
counter ! Value(this)
receive { case cvalue => Console.println(cvalue) }
This creates a new Counter actor, starts it, increments it by sending it the
Incr() message, and then sends it the Value query with the currently executing
actor this as argument. It then waits for a response of the counter actor in a
receive. Once some response is received, its value is printed (this value should
be one, unless there are other actors interacting with the counter).
Messages in this model are arbitrary objects. In contrast to channel-based pro-
gramming [11] where a channel usually has to be (generically) instantiated with
the types of messages it can handle, an actor can receive messages of any type.
In our example, actors communicate using instances of the following four message
classes.
case class Incr()
case class Value(a: Actor)
case class Lock(a: Actor)
case class UnLock(value: int)
All classes have a case modi er which enables constructor patterns for the class
(see below). Neither class has a body. The Incr class has a constructor that
takes no arguments, the Value and Lock classes have a constructor that takes
an Actor as a parameter, and the UnLock class has a constructor that takes an
integer argument.
A message send a!m sends the message m to the actor a. The communication
is asynchronous: if a is not ready to receive m, then m is queued in a mailbox of
a and the send operation terminates immediately.
Messages are processed by the receive construct, which has the following form:
receive {
case p1 => e1
:::
case pn => en
The ! method is used to send a message to an actor. The send syntax a!m
is simply an abbreviation of the method call a.!(m), just like x+y in Scala is
an abbreviation for x.+(y). The method does two things. First, it enqueues
the message argument in the actor's mailbox, which is represented as a private
eld of type List[Any]. Second, if the receiving actor is currently suspended
in a receive that could handle the sent message, the execution of the actor is
resumed.
The receive { ... } construct is more interesting. Here, the pattern matching
expression inside the braces is treated in Scala as a rst-class object that is passed
as an argument to the receive method. The argument's type is an instance of
PartialFunction, which is a subclass of Function1, the class of unary functions.
The two classes are de ned as follows.
abstract class Function1[-a,+b] {
def apply(x: a): b
}
abstract class PartialFunction[-a,+b] extends Function1[a,b] {
def isDefinedAt(x: a): boolean
}
So we see that functions are objects which have an apply method. Partial func-
tions are objects which have in addition a method isDefinedAt which can be
used to nd out whether a function is de ned at a given value. Both classes are
parameterized; the rst type parameter a indicates the function's argument type
and the second type parameter b indicates its result type4 .
A pattern matching expression { case p1 => e1 ; ...; case pn => en } is
then a partial function whose methods are de ned as follows.
{ The isDefinedAt method returns true if one of the patterns pi matches
the argument, false otherwise.
{ The apply method returns the value ei for the rst pattern pi that matches
its argument. If none of the patterns match, a MatchError exception is
thrown.
The two methods are used in the implementation of receive as follows. First,
messages m in the mailbox are scanned in the order they appear. If receive's
argument f is de ned for some of the messages, that message is removed from
the mailbox and f is applied to it. On the other hand, if f.isDefinedAt(m) is
false for every message in the mailbox, the thread associated with the actor is
suspended.
This sums up the essential implementation of thread-based actors. There is also
some other functionality in Scala's actor libraries which we have not covered.
For instance, there is a method receiveWithin which can be used to specify
a time span in which a message should be received allowing an actor to time-
out while waiting for a message. Upon timeout the action associated with a
special TIMEOUT() pattern is red. Timeouts can be used to suspend an actor,
completely ush the mailbox, or to implement priority messages [4].
Thread-based actors are useful as a higher-level abstraction of threads, which
replace error-prone shared memory accesses and locks by asynchronous message
passing. However, like threads they incur a performance penalty on standard
platforms such as the JVM, which prevents scalability. In the next section we
show how the actor model can be changed so that actors become disassociated
from threads.
4
Parameters can carry + or - variance annotations which specify the relationship
between instantiation and subtyping. The -a, +b annotations indicate that functions
are contravariant in their argument and covariant in their result. In other words
Function1[X1, Y1] is a subtype of Function1[X2, Y2] if X2 is a subtype of X1 and
Y1 is a subtype of Y2.
3 Recomposing Actors
After executing the message send p ! value, the call to receive blocks as
there are no other messages in c's mailbox. Remember that we are still inside
p's original message send (i.e. the send did not return, yet). Thus, blocking the
current thread (e.g., by issuing a call to wait()) would also block p.
This is illegal because in our programming model the send operation (!) has
a non-blocking semantics. Instead, we need to suspend c in such a way that
allows p to continue. For this, inside the (logically) blocking receive, rst, we
remember the rest of c's computation. In this case, it suces to save the closure
of
receive { case UnLock(v) => loop(v) }
Second, to let p's call of the send operation return, we need to unwind the
runtime stack up to the point where control was transferred to c. We do this by
throwing a special exception. The ! method catches this exception and returns
normally, keeping its non-blocking semantics.
In general, though, it is not sucient to save a closure to capture the rest of the
computation of an actor. For example, consider an actor executing the following
statements:
val x = receive { case y => f(y) }
g(x)
Basically, the rest of the actor's computation has to be called at the end of each
case inside the argument function of receive (\continuation passing" style).
The sent message is appended to the mailbox of the actor which is the target of
the send operation. Let A denote the target actor. If the continuation attribute
is set to a non-null value then A is suspended waiting for an appropriate message
(otherwise, A did not execute a call to receive, yet). As continuation refers
to (the closure of) the partial function with which the last blocking receive was
called, we can test if the newly appended message allows A to continue.
Note that if, instead, we would save receive(f) as continuation for a blocking
receive(f) we would not be able to test this but rather had to blindly call the
continuation. If the newly appended message would not match any of the de ned
patterns, receive would go through all messages in the mailbox again trying to
nd the rst matching message. Of course, the attempt would be in vain as only
the newly appended message could have enabled A to continue.
If A is able to process the newly arrived message we let A continue until it
blocks on a nested receive(g) or nishes its computation. In the former case,
we rst save the closure of g as A's continuation. Then, the send operation that
originated A's execution has to return because of its non-blocking semantics. For
this, the blocking receive throws a special exception of type Done (see below)
which is caught in the send method (!). Technically, this trick unwinds the call-
stack up to the point where the message send transferred control to A. Thus,
to complete the explanation of how the implementation of the send operation
works, we need to dive into the implementation of receive.
The receive method selects messages from an actor's mailbox and is responsible
for saving the continuation as well as abandoning the evaluation context:
def receive(f: PartialFunction[Any, unit]): Nothing = {
mailbox.dequeueFirst(f.isDefinedAt) match {
case Some(msg) => continuation = null
f(msg)
case None => continuation = f
}
throw new Done
}
Naturally, we dequeue the rst message in our mailbox which matches one of
the cases de ned by the partial function which is provided as an argument to
receive. Note that f.isDefinedAt has type Any => boolean. As the type of
the resulting object is Option[Any] which has two cases de ned, we can se-
lect between these cases using pattern matching. When there was a message
dequeued we rst reset the saved continuation. This is necessary to prevent a
former continuation to be called multiple times when there is a send to the
current actor inside the call f(msg).
If we didn't nd a matching message in the mailbox, we remember the continu-
ation which is the closure of f. In both cases we need to abandon the evaluation
context by throwing a special exception of type Done, so the sender which orig-
inated the call to receive can continue normally (see above).
the argument message to the mailbox unless the receiving actor is waiting for a
message and is able to process the argument message. In this case, we schedule
the continuation of the receiving actor for execution by submitting a new task
item to the scheduler.
The scheduler maintains a pool of worker threads which execute task items. A
ReceiverTask is basically a Java java.lang.Runnable that receives a speci ed
message and has an exception handler that handles requests for abandoning the
evaluation context:
class ReceiverTask(actor: Actor, msg: Any) extends Runnable {
def run(): unit =
try { actor receiveMsg msg }
catch {
case Done => // do nothing
}
}
The event-based character of our implementation stems from the fact that (1)
actors are thread-less, and (2) computations between the reception of two mes-
sages are allowed to run to completion. The second property is common for
event-driven systems [17] and re ects our assumption of a rather interactive
character for most actors. Consequently, computations between arrival of mes-
sages are expected to be rather short compared to the communication overhead.
Nevertheless, we also want to support long-running, CPU-bound actors. Such
actors should not prevent other actors from making progress. Likewise, it would
be unfortunate if a single blocking actor could cause the whole application to
stop responding, thereby hindering other actors to make progress.
We face the same problems as user-level thread libraries: Processes yield control
to the scheduler only at certain program points. In between they cannot be pre-
vented from calling blocking operations or executing in nite loops. For example,
an actor might call a native method which issues a blocking system call.
In our case, the scheduler is executed only when sending a message leads to the
resumption of another actor. Because send is not allowed to block, the receiver
(which is resumed) needs to be executed on a di erent thread. This way, the
sender is not blocked even if the receiver executes a blocking operation.
As the scheduler might not have an idle worker thread available (because all of
them are blocked), it needs to create new worker threads as needed. However, if
there is at least one worker thread runnable (i.e. busy executing an actor), we
do not create a new thread. This is to prevent the creation of too many threads
even in the absence of blocking operations.
Actors are still thread-less, though: Each time an actor is suspended because of
a blocking (which means unsuccessful) receive, instead of blocking the thread, it
is detached from its thread. The thread now becomes idle, because it has nished
executing a receiver task item. It will ask the scheduler for more work. Thereby,
threads are reused for the execution of multiple actors.
Using this method, an actor-based application with low concurrency can be
executed by as few as two threads, regardless of the number of simultaneously
active actors.
For some applications it might be worth using a scheduler which optimizes the
number of spare worker threads depending on runtime pro les. User-de ned
schedulers are easy to implement and use with our library.
In summary, additional threads are created only when needed to support (unex-
pected) blocking operations. The only blocking operation that is handled with-
out thread support is receive. Thus, a large number of non-cooperative actors
(those using blocking operations other than what our library provides), may lead
to a signi cant increase in memory consumption as the scheduler creates more
and more threads.
On the other hand, our approach adds signi cant exibility, as the library does
not need to be changed when the user decides to use a blocking operation which
has no special library support. This also means that actors and standard VM
threads can be combined seamlessly. We discovered an important use case when
porting our runtime system to use JXTA as transport layer: Providing an actor-
based interface to a thread-based library.
4 Distributed Actors
With the help of a portable runtime system actors can be executed in a dis-
tributed fashion. More speci cally, message sends are location transparent and
actors can be spawned on remote nodes. As we also target resource-constrained
devices, runtime services need to be runnable on virtual machines which o er
only a subset of the functionality of standard desktop virtual machines. For ex-
ample, the KVM6 does not support re ection. Thus, our serialization mechanism
is not based on a general re ective scheme. Instead, we provide a combinator
library which allows ecient picklers for custom datatypes to be constructed
easily. The pickler combinators are based on Kennedy's library for Haskell [19].
The generated byte arrays are compact because of (1) structure sharing, and (2)
base128 encoded integers.
Our runtime system is portable in the sense that network protocol dependent
parts are isolated in a separate layer which provides network services (connec-
tion management, message transmission, etc.). Two working prototype imple-
mentations of the service layer based on TCP and JXTA, respectively, establish
portability in practice. TCP and JXTA are protocols di erent enough that we
expect no diculties porting our runtime system to other network protocols in
the future.
We are currently working on the addition of the SOAP7 XML-over-HTTP pro-
tocol as transport layer. One of the goals is to provide an actor-based interface
to web services such as the publicly exposed APIs of Google and Amazon. More-
over, we want to build web services in terms of actors.
5 Performance Evaluation
10000
threads
tba
eba
SALSA 1.0.2
1000
Start-up time/ms
100
10
10 100 1000 10000
Number of processes
Benchmark Results. Figure 3 shows start-up times of the ring for up to 2000
processes (note that both scales are logarithmic). For event-based actors and the
nave thread-based implementation, start-up time is basically constant. Event-
based actors are about 60% slower than pure threads. However, we have reasons
to suggest that this is due to the di erent benchmark implementations. In all
actor-based implementations, start-up time is measured by starting all actors
and letting them wait to receive a special \continue" message. In contrast, the
thread-based implementation only creates all required threads without starting
them. Our measurements suggest that the used JVM optimizes thread creation,
140000
threads
tba
eba
120000 SALSA 1.0.2
100000
Number of token passes/s
80000
60000
40000
20000
0
10 100 1000 10000
Number of processes
Fig. 4.Throughput (number of token passes per second) for a xed number of
10 tokens.
6 Conclusion
References
1. Gul A. Agha. ACTORS: A Model of Concurrent Computation in Distributed Sys-
tems. Series in Arti cial Intelligence. The MIT Press, Cambridge, Massachusetts,
1986.
2. Ken Anderson, Tim Hickey, and Peter Norvig. Jscheme.
3. J. Armstrong. Erlang | a survey of the language and its industrial applications.
In INAP'96 | The 9th Exhibitions and Symposium on Industrial Applications of
Prolog, pages 16{18, Hino, Tokyo, Japan, October 1996.
4. Joe Armstrong, Robert Virding, Claes Wikstrom, and Mike Williams. Concurrent
Programming in Erlang, Second Edition. Prentice-Hall, 1996.
5. Joe L. Armstrong. The development of erlang. In ICFP, pages 196{203, 1997.
6. A. Black, M. Carlsson, M. Jones, R. Kieburtz, and J. Nordlander. Timber: A
programming language for real-time embedded systems, 2002.
7. Yannis Bres, Bernard P. Serpette, and Manuel Serrano. Bigloo.NET: compiling
scheme to.NET CLR. Journal of Object Technology, 3(9):71{94, 2004.
8. Jean-Pierre Briot. Actalk: A testbed for classifying and designing actor languages
in the smalltalk-80 environment. In ECOOP, pages 109{129, 1989.
9. Legand L. Burge III and K. M. George. JMAS: A Java-based mobile actor system
for distributed parallel computation. In Proceedings of the Fifth USENIX Confer-
ence on Object-Oriented Technologies and Systems, pages 115{129. The USENIX
Association, 1999.
10. Brian Chin and Todd Millstein. Responders: Language support for interactive
applications. In ECOOP, Nantes, France, July 2006.
11. Georgio Chrysanthakopoulos and Satnam Singh. An asynchronous messaging li-
brary for c#. In Proceedings of Workshop on Synchronization and Concurrency in
Object-Oriented Languages (SCOOL), OOPSLA, 2005.
12. Adam Dunkels, Bjorn Gronvall, and Thiemo Voigt. Contiki - A lightweight and
exible operating system for tiny networked sensors. In LCN, pages 455{462, 2004.
13. E. Gamma, R. Helm, R. Johnson, and J. Vlissides. Design Patterns. Addison-
Wesley, 1995.
14. Benoit Garbinato, Rachid Guerraoui, Jarle Hulaas, Maxime Monod, and Jesper H.
Spring. Frugal Mobile Objects. Technical report, EPFL, 2005.
15. John Gough. Compiling for the .NET Common Language Runtime. .NET series.
Prentice Hall, 2002.
16. Carl Hewitt, Peter Bishop, and Richard Steiger. A universal modular ACTOR
formalism for arti cial intelligence. In IJCAI, pages 235{245, 1973.
17. Jason Hill, Robert Szewczyk, Alec Woo, Seth Hollar, David E. Culler, and Kristofer
S. J. Pister. System architecture directions for networked sensors. In ASPLOS,
pages 93{104, 2000.
18. Richard Kelsey, William Clinger, and Jonathan Rees. Revised5 report on the
algorithmic language Scheme. Higher-Order and Symbolic Computation, 11(1):7{
105, 1998. Also appears in ACM SIGPLAN Notices 33(9), September 1998.
19. Andrew Kennedy. Pickler combinators. J. Funct. Program., 14(6):727{739, 2004.
20. George Lawton. Moving Java into mobile phones. Computer, 35(6):17{20, June
2002.
21. P. Levis and D. Culler. Mate: A tiny virtual machine for sensor networks. In
International Conference on Architectural Support for Programming Languages and
Operating Systems, San Jose, CA, USA, Oct. 2002.
22. T. Lindholm and F. Yellin. The Java Virtual Machine Speci cation. Addison-
Wesley, 1996.
23. Jeremy Manson, Jason Baker, Antonio Cunei, Suresh Jagannathan, Marek Proc-
hazka, Bin Xin, and Jan Vitek. Preemptible atomic regions for real-time java. In
RTSS, pages 62{71. IEEE Computer Society, 2005.
24. Yukihiro Matsumoto. The Ruby Programming Language. Addison Wesley Profes-
sional, 2002.
25. J. H. Nystrom, Philip W. Trinder, and David J. King. Evaluating distributed func-
tional languages for telecommunications software. In Bjarne Dacker and Thomas
Arts, editors, Proceedings of the 2003 ACM SIGPLAN Workshop on Erlang, Up-
psala, Sweden, August 29, 2003, pages 1{7. ACM, 2003.
26. Martin Odersky and al. An overview of the scala programming language. Technical
Report IC/2004/64, EPFL Lausanne, Switzerland, 2004.
27. F. Pizlo, J. M. Fox, David Holmes, and Jan Vitek. Real-time java scoped memory:
Design patterns and semantics. In ISORC, pages 101{110. IEEE Computer Society,
2004.
28. Erik Stenman and Konstantinos Sagonas. On reducing interprocess communication
overhead in concurrent programs. In Proceedings of the 2002 ACM SIGPLAN
Workshop on Erlang, Pittsburgh, PA, USA, October 7, 2002, pages 58{63. ACM,
2002.
29. D. A. Thomas, W. R. Lalonde, J. Duimovich, M. Wilson, J. McA er, and B. Berry.
Actra A multitasking/multiprocessing smalltalk. Proceedings of the ACM SIG-
PLAN Workshop on Object-Based Concurrent Programming, ACM SIGPLAN No-
tices, 24(4):87{90, April 1989.
30. Carlos Varela and Gul Agha. Programming dynamically recon gurable open sys-
tems with salsa. SIGPLAN Not., 36(12):20{34, 2001.
31. Claes Wikstrom. Distributed programming in erlang. In Hoon Hong, editor, Pro-
ceedings of the First International Symposium on Parallel Symbolic Computation,
PASCO'94 (Hagenberg/Linz, Austria, September 26-28, 1994), volume 5 of Lec-
ture Note Series in Computing, pages 412{421. World Scienti c, Singapore-New
Jersey-London-Hong Kong, 1994.