It is typically used as a blocking construct in the context of shared-memory synchronization. When using futexes, the majority of the synchronization operations are performed in user space. A user-space program employs the futex system call only when it is likely that the program has to block for a longer time until the condition becomes true. Other futex operations can be used to wake any processes or threads waiting for a particular condition. A futex is a bit value—referred to below as a futex word—whose address is supplied to the futex system call. Futexes are 32 bits in size on all platforms, including bit systems.
|Published (Last):||18 September 2008|
|PDF File Size:||6.13 Mb|
|ePub File Size:||16.52 Mb|
|Price:||Free* [*Free Regsitration Required]|
It is typically used as a blocking construct in the context of shared-memory synchronization. When using futexes, the majority of the synchronization operations are performed in user space. A user-space program employs the futex system call only when it is likely that the program has to block for a longer time until the condition becomes true.
Other futex operations can be used to wake any processes or threads waiting for a particular condition. A futex is a bit value—referred to below as a futex word—whose address is supplied to the futex system call.
Futexes are 32 bits in size on all platforms, including bit systems. All futex operations are governed by this value. In order to share a futex between processes, the futex is placed in a region of shared memory, created using for example mmap 2 or shmat 2. Thus, the futex word may have different virtual addresses in different processes, but these addresses all refer to the same location in physical memory.
In a multithreaded program, it is sufficient to place the futex word in a global variable shared by all threads. When executing a futex operation that requests to block a thread, the kernel will block only if the futex word has the value that the calling thread supplied as one of the arguments of the futex call as the expected value of the futex word.
Thus, the futex word is used to connect the synchronization in user space with the implementation of blocking by the kernel. Analogously to an atomic compare-and- exchange operation that potentially changes shared memory, blocking via a futex is an atomic compare-and-block operation. One use of futexes is for implementing locks.
The state of the lock i. In the uncontended case, a thread can access or modify the lock state with atomic instructions, for example atomically changing it from not acquired to acquired using an atomic compare-and-exchange instruction. Such instructions are performed entirely in user mode, and the kernel maintains no information about the lock state. On the other hand, a thread may be unable to acquire a lock because it is already acquired by another thread. This futex operation will block if and only if the lock is still acquired i.
When releasing the lock, a thread has to first reset the lock state to not acquired and then execute a futex operation that wakes threads blocked on the lock flag used as a futex word this can be further optimized to avoid unnecessary wake-ups.
See futex 7 for more detail on how to use futexes. Besides the basic wait and wake-up futex functionality, there are further futex operations aimed at supporting more complex use cases. Note that no explicit initialization or destruction is necessary to use futexes; the kernel maintains a futex i.
Arguments The uaddr argument points to the futex word. On all platforms, futexes are four-byte integers that must be aligned on a four-byte boundary.
The remaining arguments timeout, uaddr2, and val3 are required only for certain of the futex operations described below. Where one of these arguments is not required, it is ignored. For several blocking operations, the timeout argument is a pointer to a timespec structure that specifies a timeout for the operation.
However, notwithstanding the prototype shown above, for some operations, the least significant four bytes of this argument are instead used as an integer whose meaning is determined by the operation. Where it is required, the uaddr2 argument is a pointer to a second futex word that is employed by the operation. The interpretation of the final integer argument, val3, depends on the operation. It tells the kernel that the futex is process-private and not shared with another process i.
This allows the kernel to make some additional performance optimizations. The load of the value of the futex word is an atomic memory access i. This load, the comparison with the expected value, and starting to sleep are performed atomically and totally ordered with respect to other futex operations on the same futex word. If the thread starts to sleep, it is considered a waiter on this futex word. The purpose of the comparison with the expected value is to prevent lost wake-ups.
If the timeout is not NULL, the structure it points to specifies a timeout for the wait. This interval will be rounded up to the system clock granularity, and is guaranteed not to expire early.
If timeout is NULL, the call blocks indefinitely. This differs from other futex operations, where timeout is interpreted as an absolute value.
The arguments uaddr2 and val3 are ignored. No guarantee is provided about which waiters are awoken e. The arguments timeout, uaddr2, and val3 are ignored. The caller must close the returned file descriptor after use. The arguments timeout, uaddr2 and val3 are ignored.
The argument val3 is ignored. Otherwise, the operation wakes up a maximum of val waiters that are waiting on the futex at uaddr. If there are more than val waiters, then the remaining waiters are removed from the wait queue of the source futex at uaddr and added to the wait queue of the target futex at uaddr2.
The val2 argument specifies an upper limit on the number of waiters that are requeued to the futex at uaddr2. The load from uaddr is an atomic memory access i. This load, the comparison with val3, and the requeueing of any waiters are performed atomically and totally ordered with respect to other operations on the same futex word. Typical values to specify for val are 0 or 1. The difference is that the check of the value at uaddr can be used to ensure that requeueing happens only under certain conditions, which allows race conditions to be avoided in certain use cases.
Consider the following scenario, where multiple waiter threads are waiting on B, a wait queue implemented using a futex: lock A while! By contrast, a requeue operation wakes just one waiter and moves the other waiters to lock A, and when the woken waiter unlocks A then the next waiter can proceed. The operation and comparison that are to be performed are encoded in the bits of the argument val3.
The oparg and cmparg components are literal numeric values, except as noted below. This bit mask, in which at least one bit must be set, is stored in the kernel- internal state of the waiter. If timeout is not NULL, the structure it points to specifies an absolute timeout for the wait operation.
If timeout is NULL, the operation can block indefinitely. The uaddr2 argument is ignored. This bit mask, in which at least one bit must be set, is used to select which waiters should be woken up.
The selection is done by a bit-wise AND of the "wake" bit mask i. All of the waiters for which the result of the AND is nonzero are woken up; the remaining waiters are left sleeping. However, note that, depending on the use case, employing this bit-mask multiplexing feature on a futex can be less efficient than simply using multiple futexes, because employing bit-mask multiplexing requires the kernel to check all waiters on a futex, including those that are not interested in being woken up i.
The uaddr2 and timeout arguments are ignored. Priority-inheritance futexes Linux supports priority-inheritance PI futexes in order to handle priority-inversion problems that can be encountered with normal futex locks.
Priority inheritance is a mechanism for dealing with the priority- inversion problem. With this mechanism, when a high-priority task becomes blocked by a lock held by a low-priority task, the priority of the low-priority task is temporarily raised to that of the high- priority task, so that it is not preempted by any intermediate level tasks, and can thus make progress toward releasing the lock. With this policy in place, a user-space application can acquire an unacquired lock or release a lock using atomic instructions executed in user mode e.
If a futex is already acquired i. In the cases where callers are forced into the kernel i. After the RT-mutex is acquired, the futex value is updated accordingly, before the calling thread returns to user space. If a futex has an associated RT-mutex in the kernel i. This in turn requires that the user-space value is updated accordingly. The operation checks the value of the futex word at the address uaddr. After that, the kernel: 1. Tries to find the thread which is associated with the owner TID.
Creates or reuses kernel state on behalf of the owner. If this is the first waiter, there is no kernel state for this futex, so kernel state is created by locking the RT-mutex and the futex owner is made the owner of the RT-mutex. If there are existing waiters, then the existing state is reused.
Attaches the waiter to the futex i. If more than one waiter exists, the enqueueing of the waiter is in descending priority order. This inheritance follows the lock chain in the case of nested locking and performs deadlock detection. The timeout argument provides a timeout for the lock attempt. The uaddr2, val, and val3 arguments are ignored. It is invoked when a user-space atomic acquire did not succeed because the futex word was not 0.
Because the kernel has access to more state information than user space, acquisition of the lock might succeed if performed by the kernel in cases where the futex word i. This can happen when the owner of the futex died.
User space cannot handle this condition in a race-free manner, but the kernel can fix this up and acquire the futex. The uaddr2, val, timeout, and val3 arguments are ignored. This is called when the user-space value at uaddr cannot be changed atomically from a TID of the owner to 0. The remaining waiters are removed from the wait queue of the source futex at uaddr and added to the wait queue of the target futex at uaddr2.
The val3 argument is ignored. The idea is that these operations should always be paired, in order to ensure that user space and the kernel remain in sync. If this value is greater than val, then the difference is the number of waiters requeued to the futex for the futex word at uaddr2.
This is the sum of the woken waiters on the two futexes for the futex words at uaddr and uaddr2. If this value is greater than val, then difference is the number of waiters requeued to the futex for the futex word at uaddr2.
What you get with us