futex.mx raw

   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  	moxie_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  func (f *Futex) WaitUntil(cmp uint32, timeout uint64) {
  49  	moxie_futex_wait_timeout((*uint32)(unsafe.Pointer(&f.Uint32)), cmp, timeout)
  50  }
  51  
  52  // Wake a single waiter.
  53  func (f *Futex) Wake() {
  54  	moxie_futex_wake((*uint32)(unsafe.Pointer(&f.Uint32)))
  55  }
  56  
  57  // Wake all waiters.
  58  func (f *Futex) WakeAll() {
  59  	moxie_futex_wake_all((*uint32)(unsafe.Pointer(&f.Uint32)))
  60  }
  61  
  62  //export moxie_futex_wait
  63  func moxie_futex_wait(addr *uint32, cmp uint32)
  64  
  65  //export moxie_futex_wait_timeout
  66  func moxie_futex_wait_timeout(addr *uint32, cmp uint32, timeout uint64)
  67  
  68  //export moxie_futex_wake
  69  func moxie_futex_wake(addr *uint32)
  70  
  71  //export moxie_futex_wake_all
  72  func moxie_futex_wake_all(addr *uint32)
  73