diff --git a/src/internal/task/futex-cooperative.go b/src/internal/task/futex-cooperative.go new file mode 100644 index 0000000000..8351f88774 --- /dev/null +++ b/src/internal/task/futex-cooperative.go @@ -0,0 +1,44 @@ +package task + +// A futex is a way for userspace to wait with the pointer as the key, and for +// another thread to wake one or all waiting threads keyed on the same pointer. +// +// A futex does not change the underlying value, it only reads it before to prevent +// lost wake-ups. +type Futex struct { + Uint32 + waiters Stack +} + +// Atomically check for cmp to still be equal to the futex value and if so, go +// to sleep. Return true if we were definitely awoken by a call to Wake or +// WakeAll, and false if we can't be sure of that. +func (f *Futex) Wait(cmp uint32) (awoken bool) { + if f.Uint32.v != cmp { + return false + } + + // Push the current goroutine onto the waiter stack. + f.waiters.Push(Current()) + + // Pause until the waiters are awoken by Wake/WakeAll. + Pause() + + // We were awoken by a call to Wake or WakeAll. There is no chance for + // spurious wakeups. + return true +} + +// Wake a single waiter. +func (f *Futex) Wake() { + if t := f.waiters.Pop(); t != nil { + scheduleTask(t) + } +} + +// Wake all waiters. +func (f *Futex) WakeAll() { + for t := f.waiters.Pop(); t != nil; t = f.waiters.Pop() { + scheduleTask(t) + } +}