I’m an actor!

I quite like the actor model of concurrency and was reminded of it by a recent edition of the java posse where some concurrency experts gave their options on this and software transactional memory. In the actor model, objects communicate by messaging each other. In contrast to the shared state model of concurrency, there is no explicit locking as it is all hidden away inside the code which handles the mail boxes (where the queues could be manipulated using lock free techniques). F# offers a mailbox processor type for doing this kind of thing.
 
The typical example of this is the harmless counter which responds to messages telling it to increment itself and to pass its current value along a channel. In the following code we make the counter two state. In the initial state it appears as above, but it can be told to change to a locked state where increment isn’t allowed.
 
> #light;;
> open Microsoft.FSharp.Control.Mailboxes;;
 
Define the messages that the counter will understand.
 
> type Action= Read of IChannel<int> | Increment of int | Lock;;
type Action =
  | Read of IChannel<int>
  | Increment of int
  | Lock
 
Define the counter. The first function handles the initial state, the second function handles the state where the counter will not allow an increment.
 
> let makeCounter initialCount =
–   let count = ref initialCount
–   let counter = MailboxProcessor.Start( fun inbox ->
–    let rec increment =
–       async { let! msg = inbox.Receive()
–               match msg with
–               | Read channel ->
–                   do channel.Post(!count)
–                   return! increment
–               | Increment amount ->
–                   do count := !count + amount
–                   return! increment
–               | Lock -> return! readOnly }
–    and     readOnly =
–       async { let! msg = inbox.Receive()
–               match msg with
–               | Lock -> return! readOnly
–               | Increment _ -> do failwith "Locked"
–               | Read channel ->
–                   do channel.Post(!count)
–                   return! readOnly }
–    increment)
–   counter;;
                    return! increment
  ————————–^^^^^^^^^^
stdin(12,26): warning: FS0040: This and other recursive references will be check
ed for initialization-soundness at runtime because you are defining a recursive
object or function value, rather than a simple recursive function. This warning
is often harmless, and may be suppressed by using #nowarn "40" or –no-warn 40
val makeCounter : int -> MailboxProcessor<Action>
 
> open System;;
 
Define an instance to act as a message sink which prints any value that is sent to it.
 
> let sink =
–   { new IChannel<int>
–      with  Post (x) = printfn "Value is %d" x; };;
val sink : IChannel<int>
 
> let counter1 = makeCounter 5;;
val counter1 : MailboxProcessor<Action>
> counter1.Post (Read sink);;
Value is 5
val it : unit = ()
> counter1.Post (Increment 2);;
val it : unit = ()
> counter1.Post (Increment 2);;
val it : unit = ()
> counter1.Post (Lock);;
val it : unit = ()
> counter1.Post (Read sink);;
val it : unit = ()
Value is 9
 
The counter is now locked so when we do the following, an exception is thrown.
> counter1.Post (Increment 2);;
Advertisements
This entry was posted in Computers and Internet. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s