-
Notifications
You must be signed in to change notification settings - Fork 98
Separating Channels and Actors
For the purposes of this document/page:
-
Cloud Haskell refers to the current implementation of distributed-process - (from this repository, in fact), and other libraries authored by Duncan, Edsko, and other brilliant folks from Well Typed, and developed, maintained, and curated by the awesome people at Tweag I/O (Mathieu, Alexander, and Facundo, to mention a few), and Tim Watson
-
Akka refers to the Java/Scala implementation of Akka
-
Akka Streams refers to the Akka Scala API for Reactive Streams
-
Erlang refers to the open source implementation of Erlang/OTP
-
Actor refers to the universal primitive of concurrent computation under the actor model
-
In distributed-process terms, Actor refers to code running in the
Process
monad, which has been spawned on a local or remote node, using the primitivesspawn
,spawnLocal
,spawnChan
, and so on -
In GHC-Haskell terms, an Actor in distributed-process is a
forkIO
thread which is managed by the local node infrastructure, in terms of its lifetime, and connection(s) to other Actors in the system (whether distributed or otherwise)
Actors communicate with the outside world (and each other) using asynchronous message passing, usually offering some kind of opaque handle for third parties to use when sending messages. Examples of this include the ProcessId
type from distributed-process, and the SendPort a
type from the same library (which represents the sending end of a typed channel).
Since this form of asynchronous message passing is not a language level primitive in Haskell, we need to consider how code running outside of the Process
monad, and thus outside the context of a managed Cloud Haskell node, ought to interact with actors running within the system. One option is to use the runProcess
function, which is defined in the API for Control.Distributed.Process.Node
, which essentially does the following:
- create an empty
MVar
- spawn a new
Process
to run the supplied code (i.e.forkIO
and execute the given actor) - wait on the actor finishing (i.e. the
Process
code completing) and write()
theMVar
- have the calling thread wait on the
MVar
to determine that the actor has finished working
I've elided the details or error handling, and particularly asynchronous exception handling, and inheriting the masking state of the calling thread, and so on...
If we want to send a message to an actor in the system from outside then, we must forkIO
a new thread and use thread synchronising techniques (like MVar
) to determine that the sending completed. Since sending is asynchronous and should never fail - more in this later - we may instead choose to forkProcess
in the calling thread, since we're not waiting on a reply anyway. As long as we do not mind the following actions in the forkIO thread the spawned the new process racing with the code that sends that message, this approach is fine.