Station 02Open the adventure map
00Set up your toolsSetup01The starting lineSetup02Processes and mailboxesR1An Elixir data pipelineOptional reviewR2Read ErlangOptional review03Messages and timeouts04OTP rules for messages05A supervision tree that restarts06Put a limit on concurrency07When a BEAM node disappearsX1Agree on shared BEAM termsBilingual extensionX2Two-language partnersBilingual extension08A reliable job runner
Home/BEAM mainline/Station 02
02
FoundationBeginnerBEAMOTP

Processes and mailboxes

Each process keeps its own data. When processes need to work together, they send messages.

3checkpoints
About 3 hours · Try 3 sessionssplit it into sessions
QUESTION · ONE PROBLEM FOR THIS STATION

When several senders update a counter at once, who owns the state, and how can we spot messages that the mailbox never handles?

Start at the scene

The counter is alive, but read requests get no answer

The counter process is still running and senders keep delivering messages, but the reported value stops changing. Its loop forgot one message shape, so unmatched messages keep collecting in the mailbox.

Evidence you can observe
  • Whether the counter PID is still alive
  • Whether the mailbox length keeps growing
  • The value that stopped changing and the actual shape of unmatched mailbox messages
Why this station matters

The problem it solves

BEAM lets many small processes work at once. Each process owns its data and works with others through messages. When one process fails, the effect can usually stay in one small area.

After this station

You will be able to

  • Tell the difference between a small BEAM process, an operating-system process, and a thread

  • Explain how each process owns its data and receives messages from its mailbox

  • Read a message loop and predict its next state and pattern-match result

Before you start
  • Finish the starting line and successfully run a short Erlang or Elixir program
  • Have seen tuples, lists, and maps. It is fine if you do not remember every spelling yet
One job, two ways to write it

See what it does before how it is written

Send one tiny message first to identify PIDs, send, and receive. Then see how the counter carries new state into another round instead of changing an old value in place.

Current code
Elixir
# Remember the current IEx process so the worker has a return address
parent = self()

# spawn starts an isolated process and immediately returns its PID
worker =
  spawn(fn ->
    # The child puts its own PID in a message and sends it to parent
    send(parent, {:hello, self()})
  end)

# The parent waits in its mailbox; ^worker checks who replied
receive do
  {:hello, ^worker} -> :worker_replied
end

# total is the state currently owned by the counter process
counter = fn loop, total ->
  receive do
    # Enter the next round with a new total; the old value is not changed in place
    {:add, n} -> loop.(loop, total + n)
    {:read, caller} ->
      send(caller, {:total, total})
      loop.(loop, total)
  end
end

# Start the counter process with 0 as its first value
pid = spawn(fn -> counter.(counter, 0) end)
DESIGN DECISION

Why this shape?

Why not let every worker mutate one shared object?

Choice

One process owns the state; everyone else sends messages. Update order and failure boundaries become explicit.

Cost and boundary

Processes and messages have a cost. Pure computation without state, lifecycle, or isolation is often clearer as a function, and a process is not automatically faster.

A FAMILIAR POINT OF VIEW

Coming from Java, Python, or JavaScript

Java

Familiar starting point
Threads, virtual threads, and shared objects.
What BEAM changes
BEAM processes isolate memory by default and exchange terms through mailboxes.
False friend
Modern Java has virtual threads; state ownership and failure semantics remain the useful comparison.

Python

Familiar starting point
threading, multiprocessing, or asyncio tasks.
What BEAM changes
The VM preemptively schedules BEAM processes, each with its own mailbox.
False friend
Python has multiple runtimes and free-threaded builds; do not turn the GIL into a timeless rule.

JavaScript

Familiar starting point
An event loop, Promises, and Workers.
What BEAM changes
Many BEAM processes wait on their own mailboxes instead of sharing one application callback queue.
False friend
JavaScript Workers also isolate work; JavaScript is not simply ‘always single-threaded.’
LAB
Hands on

Send the counter three letters

About 15–25 minutes

Send “add 1,” “add 2,” and “add 3,” then read the total. Besides finding 6, identify who owns the state.

  1. 01

    Send {:add, 1}, {:add, 2}, and {:add, 3} to the counter in order

  2. 02

    Send {:read, self()} with your PID so the counter knows the return address

  3. 03

    Use Process.info(pid, :message_queue_len) to see how many messages remain in its inbox

Copy into the terminal and press Enter
# Send three addition messages; each call to send returns at once
send(pid, {:add, 1})
send(pid, {:add, 2})
send(pid, {:add, 3})

# Include your PID when asking for the current total
send(pid, {:read, self()})

# Wait for the reply; return timeout after one second
receive do
  {:total, total} -> total
after
  1_000 -> :timeout
end

# Check how many messages remain unhandled
Process.info(pid, :message_queue_len)
What you should see
  • You eventually receive {:total, 6}
  • After the read completes, message_queue_len usually returns to 0
  • Other processes can send messages, but cannot reach in and directly change the number owned by the counter
Break it on purpose

Remove the {:read, caller} branch, then send a read message. The process will not crash. That message will remain in the mailbox.

What this shows

This small process can update a count in message order while owning its own state.

What this does not show yet

This does not show that every message design is free from races, or that every mailbox will stay small forever.

Meet the words

Key ideas in this code

01

Lightweight process

An independent unit of work scheduled by BEAM. It is not a full operating-system process, so a program can create many of them.

02

mailbox

A process's own inbox. Messages wait here until the process looks for a pattern it can handle now.

03

reduction

A unit BEAM uses to estimate work. After one process has done some work, the scheduler gives other processes a chance.

Name the shape in the code

Design patterns used here

01

Actor Model

Let one process own the state and cooperate with the outside through messages.

02

Single Writer

Let one owner serialize updates instead of sharing mutable state.

03

Message Filter

Use explicit message patterns to define what is handled and observe what is not.

Think it through

What usually happens when a process mailbox contains a message that does not match any current receive pattern?

Your turn

Draw a message map for three stations

Draw sensor, collector, and dashboard. Name each message and mark its sender and receiver.

Hint 1Take the first step

Do not name every message data. Say whether it carries a temperature, a query, or a reply.

Hint 2Make it a little smaller

If a message needs a reply, include a return PID or a reference.

Hint 3You are close

Ask which messages could pile up while the dashboard is not working.

Hint 4Work backward from the finish

Choose one success signal and write the smallest test for it. If the computer cannot show the result, rewrite the signal as something you can truly observe.

Ready to move on when
  • Every message shows who sends it and who receives it

  • Every piece of state has one clear owner

  • The map marks at least one mailbox that could grow

Take these with you

Remember three things

  1. 1

    Each BEAM process owns its state and works with its partners through messages.

  2. 2

    A message loop often carries the state for the next round in recursive arguments.

  3. 3

    Messages that do not match yet remain in the mailbox, so mailbox length is worth watching.

Read a little more

Visit the original sources

Erlang ProcessesElixir Processes
Station completeYou ran the experiment and thought through the answer. Save this station.