Processes

BEAM processes are lightweight (≈2–3 KB initial memory), isolated (no shared memory), preemptively scheduled, and communicate only by message passing.

BEAM processes are lightweight (≈2–3 KB initial memory), isolated (no shared memory), preemptively scheduled, and communicate only by message passing. A single node can run millions of them.

29.1 spawn, send, receive

send/2 is asynchronous and never fails (even if the process is dead).

Each process has a mailbox; receive scans for the first message matching any pattern (selective receive). Non-matching messages stay in the mailbox.

flush() in IEx prints and empties the mailbox.

pid = spawn(fn -> IO.puts("Hello from #{inspect(self())}") end)
Process.alive?(pid)

parent = self()
spawn(fn -> send(parent, {:result, 42}) end)

receive do
  {:result, value} -> IO.puts("Got #{value}")
after
  1_000 -> IO.puts("Timeout")
end

29.2 Stateful processes via recursion

This is the essence of what GenServer abstracts.

defmodule Counter do
  def start(initial \\ 0), do: spawn(fn -> loop(initial) end)

  defp loop(count) do
    receive do
      {:increment, by} ->
        loop(count + by)
      {:get, caller} ->
        send(caller, {:count, count})
        loop(count)
      :stop ->
        :ok
    end
  end
end

pid = Counter.start()
send(pid, {:increment, 5})
send(pid, {:get, self()})
receive do {:count, c} -> c end   # 5

29.3 Process registration

Process.register(pid, :counter)
send(:counter, {:increment, 1})
Process.whereis(:counter)
Process.registered()

29.4 Process utilities

self()
Process.info(pid)                          # memory, message_queue_len, ...
Process.info(pid, :message_queue_len)
Process.sleep(100)
Process.send_after(self(), :tick, 1_000)   # timers
ref = Process.send_after(self(), :x, 5000); Process.cancel_timer(ref)
Process.exit(pid, :kill)
Process.flag(:trap_exit, true)
Process.put(:key, :value)                  # process dictionary (use rarely)
Process.get(:key)
Process.list() |> length()

29.5 References & request/response

ref = make_ref()
send(server, {:request, self(), ref, :data})
receive do
  {^ref, reply} -> reply           # pin the ref to match only our reply
after 5000 -> {:error, :timeout}
end