diff --git a/core/sync/extended.odin b/core/sync/extended.odin index 180d9b633..49d296c90 100644 --- a/core/sync/extended.odin +++ b/core/sync/extended.odin @@ -297,3 +297,59 @@ once_do :: proc(o: ^Once, fn: proc()) { do_slow(o, fn) } } + + + +// A Parker is an associated token which is initially not present: +// * The `park` procedure blocks the current thread unless or until the token +// is available, at which point the token is consumed. +// * The `park_with_timeout` procedures works the same as `park` but only +// blocks for the specified duration. +// * The `unpark` procedure automatically makes the token available if it +// was not already. +Parker :: struct { + state: Futex, +} + +// Blocks the current thread until the token is made available. +// +// Assumes this is only called by the thread that owns the Parker. +park :: proc(p: ^Parker) { + EMPTY :: 0 + NOTIFIED :: 1 + PARKED :: max(u32) + if atomic_sub_explicit(&p.state, 1, .Acquire) == NOTIFIED { + return + } + for { + futex_wait(&p.state, PARKED) + if _, ok := atomic_compare_exchange_strong_explicit(&p.state, NOTIFIED, EMPTY, .Acquire, .Acquire); ok { + return + } + } +} + +// Blocks the current thread until the token is made available, but only +// for a limited duration. +// +// Assumes this is only called by the thread that owns the Parker +park_with_timeout :: proc(p: ^Parker, duration: time.Duration) { + EMPTY :: 0 + NOTIFIED :: 1 + PARKED :: max(u32) + if atomic_sub_explicit(&p.state, 1, .Acquire) == NOTIFIED { + return + } + futex_wait_with_timeout(&p.state, PARKED, duration) + atomic_exchange_explicit(&p.state, EMPTY, .Acquire) +} + +// Automatically makes thee token available if it was not already. +unpark :: proc(p: ^Parker) { + EMPTY :: 0 + NOTIFIED :: 1 + PARKED :: max(Futex) + if atomic_exchange_explicit(&p.state, NOTIFIED, .Release) == PARKED { + futex_signal(&p.state) + } +} \ No newline at end of file