🎓 A1Learner
🔍
0 of 46 lessons completed

Elixir Atomics

An atomic counter is a small set of integer slots that many processes can update safely. Each update runs as one step that no other update can split.

Create slots and read them

A new reference holds two slots, both starting at zero. Put stores a value, and get reads it back.

ref = :atomics.new(2, [])
IO.inspect(:atomics.get(ref, 2))
:atomics.put(ref, 1, 10)
IO.inspect(:atomics.get(ref, 1))
IO.inspect(:atomics.add_get(ref, 1, 5))

Swap a slot only on a matching value

A swap from 15 to 99 succeeds and answers ok. A second swap expecting 15 fails and answers the value now held.

ref = :atomics.new(1, [])
:atomics.put(ref, 1, 15)
IO.inspect(:atomics.compare_exchange(ref, 1, 15, 99))
IO.inspect(:atomics.get(ref, 1))
IO.inspect(:atomics.compare_exchange(ref, 1, 15, 99))
IO.inspect(:atomics.get(ref, 1))

Four processes count to one hundred

Four workers each add one twenty-five times. The final read is exactly one hundred, with no update lost.

ref = :atomics.new(1, [])
parent = self()
worker = fn ->
  Enum.each(1..25, fn _n -> :atomics.add(ref, 1, 1) end)
  send(parent, :done)
end
spawn(worker)
spawn(worker)
spawn(worker)
spawn(worker)
Enum.each(1..4, fn _n ->
  receive do
    :done -> :ok
  end
end)
IO.inspect(:atomics.get(ref, 1))

Example

ref = :atomics.new(2, [])
:atomics.add_get(ref, 1, 7)
:atomics.add_get(ref, 1, 3)
:atomics.put(ref, 2, 0)
IO.inspect(:atomics.compare_exchange(ref, 2, 0, 1))
IO.inspect(:atomics.compare_exchange(ref, 2, 0, 1))
IO.inspect({:atomics.get(ref, 1), :atomics.get(ref, 2)})

Key points

  • Atomic counter: a reference holding integer slots with single-step updates.
  • Use :atomics.new/2 to create slots, with each slot starting at zero.
  • Use :atomics.add_get/3 to add and read the new value in one step.
  • Use :atomics.add/3 to add without reading, and :atomics.get/2 to read a slot.
  • Use :atomics.compare_exchange/4 to change a slot only when it still holds an expected value.

In this example, slot one counts ten requests in two single-step adds, and slot two starts at zero as a flag. The first swap sets the flag and answers ok, while the second swap expects the old flag value and answers the value now held. The final line reads both slots together.

Note

atomic slots hold integers only, never strings or maps.

Try it Yourself

Edit the code, then press Run.

Running is simulated

Exercise

A slot holds 15. What does :atomics.compare_exchange(ref, 1, 15, 99) return, and what does the slot hold after?