ref: b5ddbc86346fddc60360a817ee62bc8ca938973e
dir: /lib/thread/sem+futex.myr/
use std use "atomic" use "futex" pkg thread = /* We want to be able to read both members in the same atomic operation and this implementation assumes `ftxtag` is 32 bits. */ type sem = struct _val : ftxtag _nwaiters : uint32 ;; const mksem : (v : uint32 -> sem) const semwait : (s : sem# -> void) const semtrywait : (s : sem# -> bool) const sempost : (s : sem# -> void) ;; const mksem = {v -> [._val = (v : ftxtag)] } const semwait = {s var v = 0 xadd(&s._nwaiters, 1) for ; ; while (v = s._val) > 0 if xcas(&s._val, v, v - 1) == v xadd(&s._nwaiters, -1) -> void ;; ;; ftxwait(&s._val, v, -1) ;; } const semtrywait = {s for ; ; var v = xget(&s._val) if v == 0 -> false ;; if xcas(&s._val, v, v - 1) == v -> true ;; ;; -> false /* Unreachable */ } const sempost = {s /* It's possible for the semaphore to be deallocated at any time after `_val` is incremented so we must not dereference `s` a second time. We work around this by also reading the value of `_nwaiters` during the atomic fetch and add. */ var state = xadd((s : uint64#), 1) std.assert((state : uint32) != ~0x0, "error: semaphore overflowed\n") if (state >> 32) > 0 /* However, it is both legal and expected to pass a potentially invalid address to `ftxwake`. */ ftxwake(&s._val) ;; }