τau /

sync/atomic

module
atomic = import("sync/atomic")

atomic - reads and writes no other routine can see half of.

A mutex makes a whole section of code the property of one routine. These make a single read, write or addition indivisible, which is less and is often all that is wanted: a counter every routine bumps, a flag one routine raises and the others watch. That costs one instruction here against a pipe, a wait and a wake with sync.Mutex.

The rest of sync is written in tau because a pipe is enough to build it. This is not: the processor's instructions are the primitive, and no arrangement of pipes is one. So this module is the one place in sync with a shared object of its own - it cannot borrow the C library the way math does, since these are instructions the compiler emits inline and not functions with a name to look up.

Where a mutex would still be needed: anything touching two values at once. Atomics make each of them safe on its own and say nothing about the pair.

Int = fn(v)source

Int is an integer several routines may read and write at once.

hits = atomic.Int(0)
... in every routine:
hits.Add(1)
... after they are done:
hits.Load()

The cell is eight bytes the collector owns, so an Int costs nothing to get rid of: it goes when nothing holds it, like any other value.

Load = fn()source

Load returns what the cell holds.

Store = fn(n)source

Store writes n into the cell.

Add = fn(delta)source

Add adds delta and returns what the cell holds afterwards. That answer belongs to the caller alone: no two routines adding at once are given the same one, which is what makes it usable as a ticket.

Swap = fn(n)source

Swap writes n and returns what was there before.

CompareAndSwap = fn(old, new)source

CompareAndSwap writes new if the cell holds old, and reports whether it did. It is what a change that has to read the old value first is built from: read, work out the new one, and try until this one takes.

Bool = fn(v)source

Bool is a flag several routines may read and write at once, an Int holding 0 or 1.

stop = atomic.Bool(false)
... one routine:
stop.Store(true)
... the others:
if stop.Load() { return }

Load = fn()source

Load returns what the flag holds.

Store = fn(x)source

Store writes x into the flag.

Swap = fn(x)source

Swap writes x and returns what was there before.

CompareAndSwap = fn(old, new)source

CompareAndSwap writes new if the flag holds old, and reports whether it did.