After reading Ulrich Drepper's "Futexes are Tricky", I have written the following "dumbest mutex" in C++14 using the Linux futex
primitives. This mutex is simpler than Drepper's; I believe it to be correct. The reason it gets to be so simple is the same as the reason I'm calling it the "dumbest mutex": it makes a system call on every unlock
, even if the mutex is not being contended and nobody's waiting for the lock.
Therefore this is not a good mutex if you care about performance.
My question is: Leaving aside the performance issue, is this a correct mutex? Or does it, like most hand-written concurrency code, have some subtle bug? Stylistic comments are also welcome.
#include <atomic>
#include <linux/futex.h>
#include <syscall.h>
#include <unistd.h>
inline int futex_wait(void *addr, int block_if_value_is) {
return syscall(SYS_futex, addr, block_if_value_is, nullptr, nullptr, 0);
}
inline int futex_wake_one(void *addr) {
return syscall(SYS_futex, addr, FUTEX_WAKE, 1, nullptr, nullptr, 0);
}
class mutex {
std::atomic<int> m_state;
static constexpr int UNLOCKED = 0;
static constexpr int LOCKED = 1;
public:
constexpr mutex() noexcept : m_state(UNLOCKED) {}
bool try_lock() {
return m_state.exchange(LOCKED) == UNLOCKED;
}
void lock() {
while (m_state.exchange(LOCKED) != UNLOCKED) {
futex_wait(&m_state, LOCKED);
}
}
void unlock() {
m_state = UNLOCKED;
futex_wake_one(&m_state);
}
};