a short introduction to
Process Oriented Programming

Daniel Perez
danhper
Chiba Lab, 2017/5/14

http://bit.ly/poprog-intro

BEAM

  • Virtual machine to run Erlang
  • Native support for concurrency and distribution
  • Designed for soft real-time

Erlang processes

  • Low memory footprint
  • Fast to create/destroy
  • Low scheduling overhead
  • Share nothing

Process mailbox

  • Each process has a mailbox
  • Messages are delivered exactly once

Process state

Stateful arguments

def loop(some_int) do
  receive do
    {:add, value} ->
      loop(some_int + value)
  end
end

Creating a Stack server

defmodule StackServer do
  def loop(state) do
    receive do
      {:push, value} ->
        new_state = [value | state]
        loop(new_state)
      {:pop, sender} ->
        [value | new_state] = state
        send(sender, value)
        loop(new_state)
    end
  end
end

push function

defmodule StackServer do
  def push(pid, value) do
    send(pid, {:push, value})
  end
end

Making pop synchronous

defmodule StackServer do
  def pop(pid) do
    ref = make_ref()
    send(pid, {:pop, self(), ref})
    receive do
      {^ref, value} -> value
    end
  end

  def loop(state) do
    receive do
      {:pop, sender, ref} ->
        [value | new_state] = state
        send(sender, {ref, value})
        loop(new_state)
    end
  end
end

Error handling

Two main primitives

  • link: non-stackable bidirectional link

    if linked process dies, I must do something

  • monitor: stackable unidirectional link

    if monitored process dies, it's not really my problem

Process linking

Default behavior: if you die, I die

iex(1)> spawn_link(fn -> raise "die!" end)
** (EXIT from #PID<0.98.0>) an exception was raised:
    ** (RuntimeError) die!
        (stdlib) erl_eval.erl:668: :erl_eval.do_apply/6
 
Interactive Elixir (1.4.2) - press Ctrl+C to exit (type h() ENTER for help)
 
22:27:50.586 [error] Process #PID<0.100.0> raised an exception
** (RuntimeError) die!
    (stdlib) erl_eval.erl:668: :erl_eval.do_apply/6

Exit trapping

New link behavior: if you die, I get a message

iex(1)> Process.flag(:trap_exit, true)
false
iex(2)> spawn_link(fn -> raise "die!" end)   
#PID<0.101.0>
iex(3)> 
22:30:04.530 [error] Process #PID<0.101.0> raised an exception
** (RuntimeError) die!
    (stdlib) erl_eval.erl:668: :erl_eval.do_apply/6
flush()
{:EXIT, #PID<0.101.0>,
 {%RuntimeError{message: "die!"},
  [{:erl_eval, :do_apply, 6, [file: 'erl_eval.erl', line: 668]}]}}

Supervisor and worker

Processes are usually divided in two types

  • Worker: processes actually doing the job
  • Supervisor: processes starting and taking care of the workers

Supervisor are responsible for restarting workers

Restart strategies

4 main strategies
Given processes A,B,C, if B dies

  • One for one: restart B
  • One for all: restart A,B,C
  • Rest for one: restart B,C
  • Simple one for one: restart B

Processes in simple one for one must have the same type (i.e. call the same function) and are usually spawned dynamically

Supervision tree

Application is usually composed of many supervisors and workers