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
andWakeByAddressAll
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, afutex
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 withcompare
. 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 withcompare
. 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 withcompare
. 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
. Ifcount
is greater-than-or-equal-tomaxCount
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
. Ifcount
is greater-than-or-equal-tomaxCount
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().
-
template<class T>