FUTEX_WAKE_OP(2const)

✨ FUTEX_WAKE_OP β€” fast user-space locking ✨

okay bestie, buckle up, we're doing futexes today and honestly?? this one is a bop. it lives in section 2const of the manual (yes, FUTEX_WAKE_OP(2const), that's the whole title, she's a constant-flavored girlie πŸ’…).


πŸ“š LIBRARY

Standard C library (libc, -lc) 🫢


πŸͺ„ SYNOPSIS

#include <linux/futex.h>  /* Definition of FUTEX_* constants */
#include <sys/syscall.h>  /* Definition of SYS_* constants */
#include <unistd.h>

long syscall(SYS_futex, uint32_t *uaddr, FUTEX_WAKE_OP, uint32_t val,
             uint32_t val2, uint32_t *uaddr2,
             uint32_t val3);

πŸ’– DESCRIPTION

so this op got added to the kernel to support some user-space situations where you've gotta juggle more than one futex at the same time. the most iconic example? the implementation of pthread_cond_signal(3), which needs to touch two futexes at once β€” the one doing the mutex thing and the one chilling in the wait queue tied to the condition variable. FUTEX_WAKE_OP lets those cases happen without setting off massive rates of contention and context switching. slay.

<tada>the vibe:</tada> FUTEX_WAKE_OP is equivalent to running the code below atomically and totally ordered with respect to other futex operations on either of the two futex words you handed over:

uint32_t oldval = *(uint32_t *) uaddr2;
*(uint32_t *) uaddr2 = oldval op oparg;
futex(uaddr, FUTEX_WAKE, val, 0, 0, 0);
if (oldval cmp cmparg)
    futex(uaddr2, FUTEX_WAKE, val2, 0, 0, 0);

put another way, FUTEX_WAKE_OP does this whole routine:

  • saves the original value of the futex word at uaddr2 and performs an operation that modifies the value of the futex at uaddr2 β€” and this is an atomic read-modify-write memory access (i.e., it uses the atomic machine instructions of whatever architecture you're on, no shortcuts 😀)
  • wakes up a maximum of val waiters on the futex for the futex word at uaddr; and
  • depending on how a test of the original value of the futex word at uaddr2 goes, wakes up a maximum of val2 waiters on the futex for the futex word at uaddr2.

the operation and the comparison you want performed are both encoded in the bits of the val3 argument. picture it bestie:

+---+---+-----------+-----------+
|op |cmp|   oparg   |  cmparg   |
+---+---+-----------+-----------+
  4   4       12          12    <== # of bits

and in code form, the encoding is literally:

#define FUTEX_OP(op, oparg, cmp, cmparg) \
                (((op & 0xf) << 28) | \
                ((cmp & 0xf) << 24) | \
                ((oparg & 0xfff) << 12) | \
                (cmparg & 0xfff))

in the above, op and cmp are each one of the codes listed below πŸ‘‡ the oparg and cmparg components are literal numeric values, except as noted below.

🌸 the op component

  • FUTEX_OP_SET β€” 0 β€” /* uaddr2 = oparg; */
  • FUTEX_OP_ADD β€” 1 β€” /* uaddr2 += oparg; */
  • FUTEX_OP_OR β€” 2 β€” /* uaddr2 |= oparg; */
  • FUTEX_OP_ANDN β€” 3 β€” /* uaddr2 &= ~oparg; */
  • FUTEX_OP_XOR β€” 4 β€” /* uaddr2 ^= oparg; */

plus!! bitwise ORing the following value into op makes (1 << oparg) be used as the operand instead:

  • FUTEX_OP_ARG_SHIFT β€” 8 β€” /* Use (1 << oparg) as operand */

πŸ¦‹ the cmp field

  • FUTEX_OP_CMP_EQ β€” 0 β€” /* if (oldval == cmparg) wake */
  • FUTEX_OP_CMP_NE β€” 1 β€” /* if (oldval != cmparg) wake */
  • FUTEX_OP_CMP_LT β€” 2 β€” /* if (oldval < cmparg) wake */
  • FUTEX_OP_CMP_LE β€” 3 β€” /* if (oldval <= cmparg) wake */
  • FUTEX_OP_CMP_GT β€” 4 β€” /* if (oldval > cmparg) wake */
  • FUTEX_OP_CMP_GE β€” 5 β€” /* if (oldval >= cmparg) wake */

oh and, the return value of FUTEX_WAKE_OP is the sum of the number of waiters woken on the futex uaddr plus the number of waiters woken on the futex uaddr2. two futexes, one return value, it's giving efficiency πŸ’–


πŸŽ€ RETURN VALUE

on error, -1 gets returned, and errno is set to tell you what went wrong 😭

on success, FUTEX_WAKE_OP returns the total number of waiters that were woken up. that's the sum of the woken waiters on the two futexes for the futex words at uaddr and uaddr2. periodt.


πŸ’€ ERRORS

see futex(2).

  • EFAULT β€” uaddr2 did not point to a valid user-space address.
  • EINVAL β€” uaddr2 does not point to a valid object β€” meaning the address is not four-byte-aligned.
  • EINVAL β€” the kernel detected an inconsistency between the user-space state at uaddr and the kernel state β€” that is, it detected a waiter which waits in FUTEX_LOCK_PI(2const) or FUTEX_LOCK_PI2(2const) on uaddr.
🫠 heads up bestie: both of those last two are EINVAL, so don't go assuming you can tell them apart by the error name alone. iykyk.

⭐ STANDARDS

Linux. 🐧


🧚 HISTORY

Linux 2.6.14. (ancient in timeline terms, still iconic in behavior terms ✨)


πŸ‘‘ SEE ALSO


and that's the whole page, bestie!! 🫢 this was Linux man-pages 6.20, dated 2025-09-21 β€” go forth and futex responsibly, mwah πŸ’…πŸ’–πŸŒΈ

πŸ“¦ where this page comes from
  • arch rolling: man-pages 6.20-1 Β· BSD-2-Clause AND BSD-3-Clause AND BSD-4.3TAHOE AND BSD-4-Clause-UC AND GPL-1.0-or-later AND GPL-2.0-only AND GPL-2.0-or-later AND GPL-3.0-or-later AND LGPL-3.0-linking-exception AND LGPL-3.0-or-later AND LicenseRef-Public-Domain AND LicenseRef-UltraPermissive AND Linux-man-pages-1-para AND Linux-man-pages-copyleft AND Linux-man-pages-copyleft-2-para AND Linux-man-pages-copyleft-var AND MIT AND Spencer-94
  • nixpkgs unstable: man-pages 6.20
  • πŸ’– girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms