Concurrency

Concurrent computers work on more than one thing at once. The Wikipedia article on parallel computing provides an overview. Concurrent execution can be loosely grouped into three implementation categories, in order of loosest to tightest coupling.

The Wikipedia article on threads clarifies how threads and processes differ.

Forms of concurrency we are not covering

PLs and concurrency

Threads in general

The main Bad Things:

Locking

Avoiding races with locks of various kinds

Atomicity

Atomicity is a key design concept

Java Threads

The Java concurrency tutorial. Synchronized in detail Other Java concurrency control features. Much is in the new java.util.concurrent package. Problems with Java threads

The Actor Model

History: Hewitt's idea; elaborated by many others including yours truly.

AFbV: Actors on FbV

We will add an actor layer on top of the FbV language: AFbV. We need the "V" to have variants to define messages.

Recall FbV variants are like OCaml's inferred variants - `foo(4) is the variant foo with argument 4. Notice how we can also view this as the message foo with argument 4. FbV variants always have exactly one argument only, for simplicity.

Syntax of AFbV

AFbV expressions are the following.
e ::= ( ... all the FbV stuff ) | e <- e  | Create(e,e') | a 
where a are the atomic actor names. They are like the cells c of DS, they cannnot appear in source programs but can show up at runtime, and there are infinitely many unique ones; they are just names (nonces). Some unusual aspects of AFbV

An Example

Before getting into the operational semantics lets do an example. Here is an actor that gets a start message and then counts down from its initial value to 0:
Function myaddr ->
  Y (Function this -> Function localdata -> Function msg ->
      Match msg With
         `main(n) -> myaddr <- `count(n); this(_) 
       | `count(n) -> If n = 0 Then this(_) Else
                               myaddr <- `count(n-1);
                               this(_) /* set the function to respond to next message */
Here is a code fragment that another actor could use to fire up a new actor with the above behavior and get it started. Suppose the above code we abbreviated CountTenBeh.
Let x = create(CountTenBeh,_)  /* _ is the localdata - its unused in this example */
  In x <- `main(10)
Here is an alternative way to count down, where the localdata field holds the value, and its not in the message.
Function myaddr ->
  Y (Function this -> Function localdata -> Function msg ->
      Match msg With
        `count(_) -> If localdata = 0 Then _ Else
                               myaddr <- `count(_);
                               this(localdata - 1) /* set the function to respond to next message/
Suppose the above code was abbreviated CountTenBeh2; using it is then
Let x = create(CountTenBeh2,10)  /* 10 is  the localdata */
  In x <- `count(_)
The latter example is the correct way to give actors local data -- in the former example the counter value had to be forwarded along every message.

Here is another usage fragment for the first example:

Let x = create(CountTenBeh,_)  /* _ is the localdata - its unused in this example */
  In x <- `main(10); x <- `main(5)
In this case the actor x will in parallel and independently counting down from 10 .. 0 and 5 .. 0 - these counts may also interleave in random ways. For the second example an analogue might be:
Let x = create(CountTenBeh2,10)  /* 10 is  the localdata */
  In x <- `count(_); x <- `count(_)
This does nothing but get one more count message queued up; since the actor sends a new one out each time it gets one until 0, the effect will be to have a leftover message at the end.

Operational Semantics of Actors

The operational semantics for actors has two layers: the local computation, which is not to different than FbV, and the concurrent global stepping of all the actors. Lets do the latter first.

The Local Rules

Lets start with the local rules. They are defined with a similar relation ==> as in FbV operational semantics, but the local executions additionally have side effects of the actors they create and messages they send. We will make any such side effects be labels on this arrow relation. So we have Here then are the rules for ==>^S. Most of the rules are nearly identical to FbV, we just give the + rule to show the change:
e ==>^S n     e' ==>^S' n'
--------------------------
e + e' ==>^(S U S') n''  where  n'' is the sum of integers n and n'
- since e/e' could in theory have each created actors or sent messages, we need to append their effects to the final result. These effects are like state, they are on the side. A major difference with DS is the effects here are "write only" -- they don't change the local computation in any way, they are only spit out. In that sense local actor computation stays functional.

Here is the send rule:

e ==>^S a     e' ==>^S' v
---------------------------------
e <- e' ==>^(S U S' U {[a <-v]}) v
The main consequence is the message [a <-v] is added to the soup. (The return result v here is largely irrelevant, the goal of a message send is the side effect added to the list.)

Here is the create rule:

e ==>^S v     e' ==>^S' v'   v a v' ==>^S'' v'' 
-----------------------------------------------
Create(e, e') ==>^(S U S' U S'' U {<a,v''>}) a    for a a fresh actor name
This time the return result matters - it is the name of the new actor. The running of v a v' passes the actor its own name and its initial values to initialize it.

The Global Rule

Here is the global single-step rule for one actor in the soup processing in its entirety one message:

(G U {[a <- v']} U {<a,v>})  -->   (G U {<a,v''>} U S ) if (v v' ==>^S v'')

This is the only global rule. It matches an actor with a message in the global soup that is destined for it, uses the local semantics to run that actor (in isolation), and throws back into the soup all of the S, which contains all the messages sent by this one actor run as well as any new actors created by this one actor run. A global actor run is just the repeated application of this rule. Notice how the actor behavior which was v is changed to v'', the result of this run.

To test these rules you can run the example programs above.

The Atomicity of Actors





Last modified: Fri May 1 13:32:17 EDT 2009