🎓 A1Learner
🔍
0 of 46 lessons completed

Elixir ETS Ownership

ETS ownership is the rule that one process owns a table and controls its life. Ownership can move to another process while the rows stay in place.

The creator owns the table

A new table reports its owner as the creating process. A lookup reads a row the owner stored.

table = :ets.new(:owned, [:ordered_set, :public])
:ets.insert(table, [{1, "one"}, {2, "two"}])
IO.inspect(:ets.info(table, :owner) == self())
IO.inspect(:ets.lookup(table, 1))
:ets.delete(table)

Give a table to another process

A spawned receiver waits for the transfer message. After the handover, the old owner no longer owns the table.

parent = self()
receiver = spawn(fn ->
  receive do
    {:"ETS-TRANSFER", tid, _from, gift} ->
      IO.inspect(gift)
      IO.inspect(:ets.info(tid, :owner) == self())
      send(parent, {:took, gift})
      receive do
        :stop ->
          :ets.delete(tid)
          send(parent, :cleaned)
      end
  end
end)
table = :ets.new(:handover, [:set, :public])
:ets.insert(table, {:key, "value"})
:ets.give_away(table, receiver, :for_you)
receive do
  {:took, gift} -> IO.inspect(gift)
end
IO.inspect(:ets.info(table, :owner) == self())
send(receiver, :stop)
receive do
  :cleaned -> IO.puts("cleaned")
end

The new owner controls writes on a protected table

A protected table lets any process read but only the owner write. After the handover, the new owner adds a row the old owner can read.

parent = self()
receiver = spawn(fn ->
  receive do
    {:"ETS-TRANSFER", tid, _from, _gift} ->
      :ets.insert(tid, {3, "three"})
      send(parent, {:added, :ets.lookup(tid, 3)})
      receive do
        :stop ->
          :ets.delete(tid)
          send(parent, :cleaned)
      end
  end
end)
table = :ets.new(:guarded, [:ordered_set, :protected])
:ets.insert(table, [{1, "one"}, {2, "two"}])
:ets.give_away(table, receiver, :yours)
receive do
  {:added, row} -> IO.inspect(row)
end
IO.inspect(:ets.lookup(table, 3))
send(receiver, :stop)
receive do
  :cleaned -> IO.puts("cleaned")
end

Example

parent = self()
worker = spawn(fn ->
  receive do
    {:"ETS-TRANSFER", tid, _from, _gift} ->
      :ets.insert(tid, [{3, "three"}])
      send(parent, :ready)
      receive do
        :stop ->
          :ets.delete(tid)
          send(parent, :cleaned)
      end
  end
end)
table = :ets.new(:shared, [:ordered_set, :public])
:ets.insert(table, [{1, "one"}, {2, "two"}])
:ets.give_away(table, worker, :yours)
receive do
  :ready -> IO.inspect(:ets.tab2list(table))
end
send(worker, :stop)
receive do
  :cleaned -> IO.puts("cleaned")
end

Key points

  • Owner: the process that created a table and decides when it ends.
  • Use :ets.info(table, :owner) to check which process owns a table.
  • Use :ets.give_away/3 to hand a table to another process with a gift value.
  • The new owner receives a transfer message naming the table and the gift.
  • Ownership moves the control of the table, not the rows.

In this example, the parent creates a table with two rows and hands it to a worker with a gift value. The worker adds a third row and reports ready, and the parent reads the full table in key order. The parent then stops the worker, which deletes the table and reports cleaned.

Note

a table ends when its owner ends, unless ownership moved first.

Try it Yourself

Edit the code, then press Run.

Running is simulated

Exercise

After :ets.give_away(table, worker, :yours), which process owns the table?