Case Study: Linux Futex (Fast Userspace Mutex)
#include <linux/futex.h> #include <sys/time.h> int futex(int *uaddr, int futex_op, int val, const struct timespec *timeout);
uaddrpoints to a 32-bit value in user space
futex_op
FUTEX_WAIT– ifval == *uaddrthen sleep till FUTEX_WAKE Atomic check that condition still holds after we disable interrupts (in kernel!)
FUTEX_WAKE– wake up at mostvalwaiting threads
FUTEX_FD,FUTEX_WAKE_OP,FUTEX_CMP_REQUEUE: More interesting operations!
timeout-ptrto a timespec structure that specifies a timeout for the op
Interface to the kernel sleep() functionality. Let the thread put themselves to sleep conditionally.
T&S and Futex
acquire(int *thelock) {
while(__atomic_test_and_set(thelock, __ATOMIC_SEQ_CST)) {
futex(thelock, FUTEX_WAIT, 1);
}
}
release(int *thelock) {
thelock = 0;
futex(thelock, FUTEX_WAKE, 1);
}acquire(int *thelock) This function is used to acquire the lock. The argument is a pointer to the lock variable.
futex(thelock, FUTEX_WAIT, 1)is called when the lock is not available. This puts the calling thread to sleep until the lock becomes available. TheFUTEX_WAIToperation suspends the thread if the current value of the futex word (i.e., the lock variable) is1(the third argument tofutex).
release(int *thelock) This function is used to release the lock. The argument is a pointer to the lock variable.
futex(&thelock, FUTEX_WAKE, 1);wakes up one of the threads waiting on the lock, if any. TheFUTEX_WAKEoperation wakes up a number of threads waiting on the futex word (i.e., the lock variable). The third argument tofutexspecifies the maximum number of threads to wake up, which in this case is1.
There is no busy waiting here. The acquire procedure simply sleeps until being waken up by the release. But we still have to tap into the kernel to release the lock even if there is no one who is acquring the lock.
So we provide a second implementation to be syscall-free in the uncontended case:
A more elegant implementation:
Last updated