Skip to content

Latest commit

 

History

History
178 lines (130 loc) · 3.67 KB

10_MessagePassingConcurrency.md

File metadata and controls

178 lines (130 loc) · 3.67 KB

Message Passing Concurrency

[ToC]

About Actor Model

Professor Carl Hewitt published the famous paper Actor model of computation in 1974. In the thesis, he elaborates that:

An Actor is a computational entity that, in response to a message it receives, can concurrently:

  • send a finite number of messages to other Actors;
  • create a finite number of new Actors;
  • designate the behavior to be used for the next message it receives.

With the rise of multi-core computing and large-scale distributed systems, the Actor Model is becoming increasingly important because of its natural concurrent, parallel, and distributed.

Process and Mailbox

An actor in Hamler/Erlang is defined as a process, which works like an OS process. Each process has its own memory, composed of a mailbox, a heap, a stack and a process control block(PCB) with information about the process.

Process

Processes in Erlang are very lightweight. We can create millions of processes on a running Erlang virtual machine.

A process is identified by a Pid. Other processes can send messages to a process via Pid.

A Ping/Pong Example

import Prelude

go :: Process ()
go = do
  self <- getSelf
  pid <- spawn loop
  pid ! (self, :ping)
  receive
    :pong -> println "Pong!"
  pid ! :stop

loop :: Process ()
loop =
  receive
    (from, :ping) -> do
      println "Ping!"
      from ! :pong
      loop
    :stop -> return ()

Spawn a new process

In Hamler, a new process is created via the spawn functions, which are defined in Control.Process.Spawn module.

-- | Create a process
spawn :: forall a. IO a -> Process Pid

-- | Create and link a process
spawnLink :: forall a. IO a -> Process Pid

-- | Create and monitor a process
spawnMonitor :: forall a. IO a -> Process (Pid, Ref)

Send/Receive message

go :: Process ()
go = do
  pid <- spawn recv
  pid ! :msg

recv :: Process ()
recv = receive x -> printf "recv: %s" (showAny x)

Selective Receive

go :: Process ()
go = do
  pid <- spawn selectiveRecv
  pid ! :bar
  pid ! :foo

selectiveRecv :: Process ()
selectiveRecv = do
  receive :foo -> println "foo"
  receive :bar -> println "bar"

Receive ... after

go :: Process ()
go = do
  pid <- spawn recvAfter
  pid ! :foo

recvAfter :: Process ()
recvAfter =
  receive
    :bar -> println "recv bar"
  after
    1000 -> println "timeout"

Registered Processes

A process can be registered under a name, which has an Atom type.

import Control.Process (register, whereis, unregister)

go :: Process ()
go = do
  pid <- spawn proc
  register :name pid
  res <- whereis :name
  unregister :name

proc :: Process ()
proc = receive _ -> return ()

Linking

Two processes can be linked to each other.

import Prelude
import Control.Process (killProc, trapExit)

go :: forall a. Process a
go = do
  trapExit true
  pid <- spawn (receive _ -> return ())
  link pid
  killProc pid
  receive msg -> return msg

Monitoring

One process Pid1 can monitor another process Pid2. If Pid2 terminates, Pid1 will receive a 'DOWN' message from Pid2.

import Prelude
import Control.Process (killProc)

go :: forall a. IO a
go = do
  pid <- spawn proc
  ref <- monitor pid
  killProc pid
  receive msg -> return msg

proc :: Process ()
proc = receive _ -> return ()

Process Termination

import Prelude
import Control.Process (isAlive, exitProc)

-- Exit the current process
exit :normal

go :: Process Boolean
go = do
  pid <- spawn (receive _ -> return ())
  exitProc pid :reason
  isAlive pid