|  | 
|  | 1 | +package futex | 
|  | 2 | + | 
|  | 3 | +// Cross platform futex implementation. | 
|  | 4 | +// Futexes are supported on all major operating systems and on WebAssembly. | 
|  | 5 | +// | 
|  | 6 | +// For more information, see: https://outerproduct.net/futex-dictionary.html | 
|  | 7 | + | 
|  | 8 | +import ( | 
|  | 9 | +	"sync/atomic" | 
|  | 10 | +	"unsafe" | 
|  | 11 | +) | 
|  | 12 | + | 
|  | 13 | +// A futex is a way for userspace to wait with the pointer as the key, and for | 
|  | 14 | +// another thread to wake one or all waiting threads keyed on the same pointer. | 
|  | 15 | +// | 
|  | 16 | +// A futex does not change the underlying value, it only reads it before going | 
|  | 17 | +// to sleep (atomically) to prevent lost wake-ups. | 
|  | 18 | +type Futex struct { | 
|  | 19 | +	atomic.Uint32 | 
|  | 20 | +} | 
|  | 21 | + | 
|  | 22 | +// Atomically check for cmp to still be equal to the futex value and if so, go | 
|  | 23 | +// to sleep. Return true if we were definitely awoken by a call to Wake or | 
|  | 24 | +// WakeAll, and false if we can't be sure of that. | 
|  | 25 | +func (f *Futex) Wait(cmp uint32) bool { | 
|  | 26 | +	tinygo_futex_wait((*uint32)(unsafe.Pointer(&f.Uint32)), cmp) | 
|  | 27 | + | 
|  | 28 | +	// We *could* detect a zero return value from the futex system call which | 
|  | 29 | +	// would indicate we got awoken by a Wake or WakeAll call. However, this is | 
|  | 30 | +	// what the manual page has to say: | 
|  | 31 | +	// | 
|  | 32 | +	// > Note that a wake-up can also be caused by common futex usage patterns | 
|  | 33 | +	// > in unrelated code that happened to have previously used the futex | 
|  | 34 | +	// > word's memory location (e.g., typical futex-based implementations of | 
|  | 35 | +	// > Pthreads mutexes can cause this under some conditions). Therefore, | 
|  | 36 | +	// > callers should always conservatively assume that a return value of 0 | 
|  | 37 | +	// > can mean a spurious wake-up, and use the futex word's value (i.e., the | 
|  | 38 | +	// > user-space synchronization scheme) to decide whether to continue to | 
|  | 39 | +	// > block or not. | 
|  | 40 | +	// | 
|  | 41 | +	// I'm not sure whether we do anything like pthread does, so to be on the | 
|  | 42 | +	// safe side we say we don't know whether the wakeup was spurious or not and | 
|  | 43 | +	// return false. | 
|  | 44 | +	return false | 
|  | 45 | +} | 
|  | 46 | + | 
|  | 47 | +// Like Wait, but times out after the number of nanoseconds in timeout. | 
|  | 48 | +// If timeout is 0, it may or may not be treated as Wait with infinite timeout. | 
|  | 49 | +// Therefore, make sure the timeout value is non-zero. | 
|  | 50 | +func (f *Futex) WaitUntil(cmp uint32, timeout uint64) { | 
|  | 51 | +	tinygo_futex_wait_timeout((*uint32)(unsafe.Pointer(&f.Uint32)), cmp, timeout) | 
|  | 52 | +} | 
|  | 53 | + | 
|  | 54 | +// Wake a single waiter. | 
|  | 55 | +func (f *Futex) Wake() { | 
|  | 56 | +	tinygo_futex_wake((*uint32)(unsafe.Pointer(&f.Uint32))) | 
|  | 57 | +} | 
|  | 58 | + | 
|  | 59 | +// Wake all waiters. | 
|  | 60 | +func (f *Futex) WakeAll() { | 
|  | 61 | +	tinygo_futex_wake_all((*uint32)(unsafe.Pointer(&f.Uint32))) | 
|  | 62 | +} | 
|  | 63 | + | 
|  | 64 | +//export tinygo_futex_wait | 
|  | 65 | +func tinygo_futex_wait(addr *uint32, cmp uint32) | 
|  | 66 | + | 
|  | 67 | +//export tinygo_futex_wait_timeout | 
|  | 68 | +func tinygo_futex_wait_timeout(addr *uint32, cmp uint32, timeout uint64) | 
|  | 69 | + | 
|  | 70 | +//export tinygo_futex_wake | 
|  | 71 | +func tinygo_futex_wake(addr *uint32) | 
|  | 72 | + | 
|  | 73 | +//export tinygo_futex_wake_all | 
|  | 74 | +func tinygo_futex_wake_all(addr *uint32) | 
0 commit comments