The FUTEX_WAKE_OP Operation: Atomically Waking One Waiter and Modifying a Second Futex in a Single Syscall

2026-07-13

Every futex operation you've probably used — FUTEX_WAIT, FUTEX_WAKE — does one thing. But FUTEX_WAKE_OP does two things atomically: it modifies the value at a second futex address, and then conditionally wakes waiters on either or both futexes based on the old value. It exists for exactly one reason: to make pthread_cond_signal() not suck.

The syscall takes two futex addresses (uaddr, uaddr2) and an encoded 32-bit val3 that packs four fields: an op (SET, ADD, OR, ANDN, XOR), an oparg, a cmp (EQ, NE, LT, LE, GT, GE), and a cmparg. The kernel atomically loads *uaddr2, applies op(oldval, oparg) to store the new value, and then wakes up to val waiters on uaddr. If cmp(oldval, cmparg) is true, it also wakes up to val2 waiters on uaddr2.

Why glibc needs this: A condition variable has two internal futexes — one that threads sleep on inside pthread_cond_wait(), and the associated mutex. Without WAKE_OP, signaling a condvar while a thread is racing between "released mutex" and "started waiting" required either extra syscalls or a lost-wakeup risk. With WAKE_OP, glibc bumps the condvar sequence counter and wakes waiters in one atomic kernel operation — no window where state is inconsistent.

Concrete example — the classic condvar signal path:

One syscall replaces what would otherwise be a store, a compare, and up to two FUTEX_WAKE calls. On a contended condvar, that's roughly 3 syscalls collapsed into 1 — saving ~500ns per signal on modern hardware once you count syscall entry/exit and the kernel taking the futex hash bucket lock only once instead of three times.

Rule of thumb: if you find yourself doing "atomically modify X then wake waiters on Y" in userspace synchronization primitives, FUTEX_WAKE_OP is the primitive you want — but almost nobody outside libc implementers uses it directly. It's the invisible plumbing that makes pthread_cond_signal a single trap instead of a race-prone dance.

Key Takeaway: FUTEX_WAKE_OP collapses "modify one futex, wake waiters on another" into a single atomic syscall — the hidden mechanism that makes pthread_cond_signal both correct and fast.

All newsletters