wasm_wait (wasm v0.1.0)
View Sourcememory.atomic.wait and memory.atomic.notify. Read this when a threaded
guest is parked and you want to know who is meant to wake it.
A waiting agent parks until another writes the address it is watching and
notifies it. On other runtimes this is a futex; here it is an Erlang process
blocking in receive, which is the one part of the threads proposal the BEAM
makes easier rather than harder.
The race, and why the mailbox settles it
A futex has a famous hazard. The waiter must check the value and then sleep, and a notifier that writes and signals between those two steps signals nobody: the waiter sleeps forever holding a value it already knows is stale. Real implementations close it with a lock around the queue, or with a kernel that performs the compare and the sleep as one operation.
Here the waiter registers before it re-reads the value. A notify arriving
after that lands in the waiter's mailbox, and receive finds it whether it
arrived before or after the call. There is nothing to lose, because a mailbox
is not a signal that can be missed; it is a queue.
Each wait carries a fresh reference, so a notify that arrives after a waiter gave up cannot be mistaken for an answer to that waiter's next wait. The mailbox is swept on the way out either way, so a stale message cannot accumulate.
What is not modelled
Nothing here reorders memory. Every access goes through atomics, which is
sequentially consistent, so atomic.fence has nothing to do and the relaxed
memory model the proposal permits is not exploited. That is a choice worth
naming: an implementation that reordered would be faster and would also make
the ordering bugs in guest programs unreproducible.
Summary
Functions
Wake up to Count agents waiting on Addr, answering how many.
Park until notified, the value changes, or the timeout expires.
Functions
-spec notify(term(), non_neg_integer(), non_neg_integer()) -> non_neg_integer().
Wake up to Count agents waiting on Addr, answering how many.
-spec wait(term(), non_neg_integer(), fun(() -> integer()), integer()) -> 0 | 1 | 2.
Park until notified, the value changes, or the timeout expires.
Answers 0 if woken, 1 if the value at the address was not the expected one, and 2 if it timed out. A negative timeout means no timeout at all.