carb::thread::futex

Defined in carb/thread/Futex.h

struct futex

Futex namespace.

FUTEX stands for Fast Userspace muTEX. Put simply, it’s a way of efficiently blocking threads by waiting on an address as long as the value at that address matches the expected value. Atomically the value at the address is checked and if it matches the expected value, the thread enters a wait-state (until notified). If the value does not match expectation, the thread does not enter a wait-state. This low-level system is the foundation for many synchronization primitives.

On Windows, this functionality is achieved through APIs WaitOnAddress, WakeByAddressSingle and WakeByAddressAll and support values of 1, 2, 4 or 8 bytes. Much of the functionality is implemented without requiring system calls, so attempting to wait when the value is different, or notifying without any waiting threads are very efficient—only a few nanoseconds each. Calls which wait or notify waiting threads will enter the kernel and be on the order of microseconds.

The Linux kernel provides a futex syscall for this functionality, with two downsides. First, a futex can be only four bytes (32-bit), and second due to being a syscall even calls with no work can take nearly a microsecond. macOS has a similar feature in the undocumented __ulock_wait and __ulock_wake calls.

For Linux and macOS, the Carbonite futex system has a user-space layer called ParkingLot that supports values of 1, 2, 4 or 8 bytes and eliminates most syscalls unless work must actually be done. This causes no-op work to be on the order of just a few nanoseconds with worst-case timing being comparable to syscall times.

Linux information: http://man7.org/linux/man-pages/man2/futex.2.html

Windows information: https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitonaddress

Warning

Futex is a very low-level system; generally its use should be avoided. There are plenty of higher level synchronization primitives built on top of Futex that should be used instead, such as carb::cpp::atomic.

Public Static Functions

template<class T>
static inline void wait(const std::atomic<T> &val, T compare) noexcept

Waits on a value until woken.

The value at val is atomically compared with compare. If the values are not equal, this function returns immediately. Otherwise, if the values are equal, this function sleeps the current thread. Waking is not automatic when the value changes. The thread that changes the value must then call wake() to wake the waiting threads.

Note

Futexes are prone to spurious wakeups. It is the responsibility of the caller to determine whether a return from wait() is spurious or valid.

Parameters
  • val – The value that is read atomically. If this matches compare, the thread sleeps.

  • compare – The expected value.

template<class T, class Rep, class Period>
static inline bool wait_for(const std::atomic<T> &val, T compare, std::chrono::duration<Rep, Period> duration)

Waits on a value until woken or timed out.

The value at val is atomically compared with compare. If the values are not equal, this function returns immediately. Otherwise, if the values are equal, this function sleeps the current thread. Waking is not automatic when the value changes. The thread that changes the value must then call wake() to wake the waiting threads.

Note

Futexes are prone to spurious wakeups. It is the responsibility of the caller to determine whether a return from wait() is spurious or valid.

Note

On Linux, interruptions by signals are treated as spurious wakeups.

Parameters
  • val – The value that is read atomically. If this matches compare, the thread sleeps.

  • compare – The expected value.

  • duration – The relative time to wait.

Returns

true if woken legitimately or spuriously; false if timed out.

template<class T, class Clock, class Duration>
static inline bool wait_until(const std::atomic<T> &val, T compare, std::chrono::time_point<Clock, Duration> time_point)

Waits on a value until woken or timed out.

The value at val is atomically compared with compare. If the values are not equal, this function returns immediately. Otherwise, if the values are equal, this function sleeps the current thread. Waking is not automatic when the value changes. The thread that changes the value must then call wake() to wake the waiting threads.

Note

Futexes are prone to spurious wakeups. It is the responsibility of the caller to determine whether a return from wait() is spurious or valid.

Parameters
  • val – The value that is read atomically. If this matches compare, the thread sleeps.

  • compare – The expected value.

  • time_point – The absolute time point to wait until.

Returns

true if woken legitimately or spuriously; false if timed out.

template<class T>
static inline void notify(std::atomic<T> &val, unsigned count, unsigned maxCount = unsigned(INT_MAX)) noexcept

Wakes threads that are waiting in one of the futex wait functions.

Note

To wake all threads waiting on val, use wake_all().

Parameters
  • val – The same value that was passed to wait(), wait_for() or wait_until().

  • count – The number of threads to wake. To wake all threads, use wake_all().

  • maxCount – An optimization for Windows that specifies the total number of threads that are waiting on addr. If count is greater-than-or-equal-to maxCount then a specific API call that wakes all threads is used. Ignored on Linux.

template<class T>
static inline void wake(std::atomic<T> &val, unsigned count, unsigned maxCount = unsigned(INT_MAX)) noexcept

Wakes threads that are waiting in one of the futex wait functions.

Note

To wake all threads waiting on val, use wake_all().

Parameters
  • val – The same value that was passed to wait(), wait_for() or wait_until().

  • count – The number of threads to wake. To wake all threads, use wake_all().

  • maxCount – An optimization for Windows that specifies the total number of threads that are waiting on addr. If count is greater-than-or-equal-to maxCount then a specific API call that wakes all threads is used. Ignored on Linux.

template<class T>
static inline void notify_one(std::atomic<T> &val) noexcept

Wakes one thread that is waiting in one of the futex wait functions.

Parameters

val – The same value that was passed to wait(), wait_for() or wait_until().

template<class T>
static inline void wake_one(std::atomic<T> &val) noexcept

Wakes one thread that is waiting in one of the futex wait functions.

Parameters

val – The same value that was passed to wait(), wait_for() or wait_until().

template<class T>
static inline void notify_all(std::atomic<T> &val) noexcept

Wakes all threads that are waiting in one of the futex wait functions.

Parameters

val – The same value that was passed to wait(), wait_for() or wait_until().

template<class T>
static inline void wake_all(std::atomic<T> &val) noexcept

Wakes all threads that are waiting in one of the futex wait functions.

Parameters

val – The same value that was passed to wait(), wait_for() or wait_until().