.\" Copyright, the authors of the Linux man-pages project
.\"
.\" SPDX-License-Identifier: MIT
.\"
.TH futex_waitv 2 2026-02-18 "Linux man-pages 6.18"
.SH NAME
futex_waitv \- wait for FUTEX_WAKE operation on a vector of futexes
.SH LIBRARY
Standard C library
.RI ( libc ,\~ \-lc )
.SH SYNOPSIS
.nf
.BR "#include <linux/futex.h>" "  /* Definition of " FUTEX* " constants */"
.BR "#include <sys/syscall.h>" "  /* Definition of " SYS_* " constants */"
.B #include <unistd.h>
.B #include <time.h>
.P
.BR long\~syscall( unsigned\~int\~n;
.BI "             SYS_futex_waitv, struct\~futex_waitv\~" waiters [ n ],
.BI "             unsigned\~int\~" n ", unsigned\~int\~" flags ,
.BI "             const\~struct\~timespec\~*_Nullable\~" timeout ", clockid_t\~" clockid );
.fi
.P
.EX
.B "#include <linux/futex.h>"
.P
struct futex_waitv {
	u64  val;         /* Expected value at \f[I]uaddr\f[] */
	u64  uaddr;       /* User address to wait on */
	u32  flags;       /* Flags for this waiter */
	u32  __reserved;  /* Align to u64 */
};
.EE
.SH DESCRIPTION
.\" This name is used internally in the kernel
Implements the FUTEX_WAIT_MULTIPLE operation,
analogous to a synchronous atomic parallel
.BR FUTEX_WAIT (2const)
or
.B FUTEX_WAIT_PRIVATE
on up to
.B FUTEX_WAITV_MAX
futex words.
For an overview of futexes, see
.BR futex (7);
for a description of the general interface, see
.BR futex (2);
for general minutiae of futex waiting, see the page above.
.P
This operation tests that the values at the
futex words
.IR waiters []. uaddr[0]
still contain respective expected values
.IR waiters []. val ,
and if so, sleeps waiting for a
.BR FUTEX_WAKE (2const)
operation on any of the futex words,
and returns the index of
.I a
waiter whose futex was woken.
.P
If the thread starts to sleep,
it is considered a waiter on all given futex words.
If any of the futex values do not match their respective
.IR waiters []. val ,
the call fails immediately with the error
.BR EAGAIN .
.P
If
.I timeout
is NULL,
the call blocks indefinitely.
Otherwise,
.I *timeout
specifies a deadline measured against clock
.IR clockid .
This interval will be rounded up to the system clock granularity,
and is guaranteed not to expire early.
.P
The following clocks are supported:
.TP
.B CLOCK_MONOTONIC
.TQ
.B CLOCK_REALTIME
.P
Futex words to monitor are given by
.IR "struct futex_waitv" ,
whose fields are analogous to
.BR FUTEX_WAIT (2const)
parameters, except
.I .__reserved
must be 0
and
.I .flags
must contain exactly one size flag,
ORed with some other flags.
.TP
.B FUTEX2_SIZE_U32
.I .val
and
.I .uaddr[]
are 32-bit unsigned integers.
.TP
.B FUTEX2_SIZE_U8
.TQ
.B FUTEX2_SIZE_U16
.TQ
.B FUTEX2_SIZE_U64
These are defined,
but not supported
.RB ( EINVAL ).
.TP
.B FUTEX2_NUMA
The futex word is followed by another word of the same size
.RI ( .uaddr
points to
.IR uint N _t[2]
rather than
.IR uint N _t .
The word is given by
.IR .uaddr[1] ),
which can be either
.B FUTEX_NO_NODE
(all bits set)
or a NUMA node number.
.IP
If the NUMA word is
.BR FUTEX_NO_NODE ,
the node number of the processor the syscall executes on is written to it.
(Except that in an
.B EINVAL
or
.B EFAULT
condition, this happens to all waiters whose
.I .flags
have
.B FUTEX2_NUMA
set.)
.IP
Futexes are placed on the NUMA node given by the NUMA word.
Futexes without this flag are placed on a random node.
.\" linux.git cec199c5e39b (2025-05-03; "futex: Implement FUTEX2_NUMA")
.\"
.\" FUTEX2_MPOL is not documented or used anywhere;
.\" it's unclear to me what it does (defined in
.\" linux.git c042c505210d (2025-05-03; "futex: Implement FUTEX2_MPOL"))
.TP
.B FUTEX2_PRIVATE
By default, the futex is shared
(like
.BR FUTEX_WAIT (2const)),
and can be accessed by multiple processes;
this flag waits on a private futex word,
where all users must use the same virtual memory map
(like
.BR FUTEX_WAIT_PRIVATE ;
this most often means they are part of the same process).
Private futexes are faster than shared ones.
.P
Programs should assign to
.I .uaddr
by casting a pointer to
.IR uintptr_t .
.\"
.\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
.\"
.SH RETURN VALUE
Returns an index to an arbitrary entry in
.I waiters
corresponding to some woken-up futex.
This implies no information about other waiters.
.P
On error,
\-1 is returned,
and
.I errno
is set to indicate the error.
.\"
.\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
.\"
.SH ERRORS
.TP
.B EFAULT
.I waiters
points outside the accessible address space.
.TP
.B EFAULT
.I timeout
is not NULL and points outside the accessible address space.
.TP
.B EFAULT
Any
.IR waiters []. uaddr
field points outside the accessible address space.
.TP
.B EINVAL
Any
.IR waiters []. uaddr
field does not point to a valid object\[em]that is,
the address is not aligned appropriately for the specified
.BI FUTEX2_SIZE_ * .
.TP
.B EINVAL
.I flags
was not 0.
.TP
.B EINVAL
.I n
was not in the range
.RB [ 1 ,
.BR FUTEX_WAITV_MAX ].
.TP
.B EINVAL
.I timeout
was not NULL and
.I clockid
was not a valid clock.
.TP
.B EINVAL
.I *timeout
is denormal (before epoch or
.I tv_nsec
not in the range
.RB [ 0 ,
.BR 999\[aq]999\[aq]999 ]).
.TP
.B EINVAL
Any
.IR waiters []. flags
field contains an unknown flag.
.TP
.B EINVAL
Any
.IR waiters []. flags
field does not contain exactly one size flag,
or it contains an unsupported one.
.TP
.B EINVAL
Any
.IR waiters []. __reserved
field is not 0.
.TP
.B EINVAL
Any
.IR waiters []. value
field has more bits set than permitted than the size flags.
.TP
.B EINVAL
.B FUTEX2_NUMA
was set in
.IR waiters []. flags ,
and the NUMA word
(which is the same size as the futex word)
is too small to contain the highest possible index of a NUMA domain
(for example,
.B FUTEX2_SIZE_U8
and there are at least 255 possible NUMA domains).
.TP
.B EINVAL
.B FUTEX2_NUMA
was set in
.IR waiters []. flags ,
and the NUMA word is larger than the maximum possible NUMA node and not
.BR FUTEX_NO_NODE .
.TP
.B ETIMEDOUT
The timeout elapsed before any futex was woken.
.TP
.BR EAGAIN " or " EWOULDBLOCK
The value in
.I .uaddr[0]
was not equal to the expected value
.I .val
at the time of the call.
.TP
.B EINTR
The
operation was interrupted by a signal (see
.BR signal (7)).
.\"
.\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
.\"
.SH STANDARDS
Linux.
.SH HISTORY
.\" commit bf69bad38cf63d980e8a603f8d1bd1f85b5ed3d9
.\" Author: André Almeida <andrealmeid@igalia.com>
.\" Date:   Thu Sep 23 14:11:05 2021 -0300
.\"
.\"     futex: Implement sys_futex_waitv()
Linux 5.16.
.SH EXAMPLES
The program below executes a linear-time operation on 10 threads,
displaying the results in real time,
waiting at most 1 second for each new result.
The first 3 threads operate on the same data (complete in the same time).
.B !\&
indicates the futex that woke up each
.BR futex_waitv ().
.P
.in +4
.EX
.RB $\~ ./futex_waitv
153	153	153	237	100	245	177	127	215	61
									122!
				200!
							254!
306	306!
		306!
						354!
								430!
			474!
					490!
futex_waitv: my_futex_waitv: Connection timed out
.EE
.P
.\" SRC BEGIN (futex_waitv.c)
.EX
#include <err.h>
#include <errno.h>
#include <linux/futex.h>
#include <pthread.h>
#include <stdatomic.h>
#include <stdcountof.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/syscall.h>
#include <time.h>
#include <unistd.h>
\&
static inline long
my_futex_wake_private(_Atomic uint32_t *uaddr, uint32_t val)
{
	return syscall(SYS_futex, uaddr, FUTEX_WAKE_PRIVATE, val);
}
\&
static inline long
my_futex_waitv(unsigned int n;
               struct futex_waitv waiters[n], unsigned int n,
               unsigned int flags, const struct timespec *timeout,
               clockid_t clockid)
{
	return syscall(SYS_futex_waitv, waiters, n, flags, timeout, clockid);
}
\&
void *
worker(void *arg)
{
	_Atomic uint32_t  *futex = arg;
\&
	usleep(*futex * 10000);
	*futex *= 2;
	my_futex_wait_private(futex, 1);
	return NULL;
}
\&
int
main(void)
{
	_Atomic uint32_t    futexes[10];
	uint8_t             init[countof(futexes)];
	struct futex_waitv  waiters[countof(futexes)] = {};
\&
	if (getentropy(init, sizeof(init)))
		err(EXIT_FAILURE, "getentropy");
	init[0] = init[1] = init[2];
	for (int i = 0; i < countof(futexes); ++i) {
		printf("%w8u\[rs]t", init[i]);
		atomic_init(&futexes[i], init[i]);
		pthread_create(&(pthread_t){}, NULL, worker, &futexes[i]);
	}
	putchar(\[aq]\[rs]n\[aq]);
\&
	for (int i = 0; i < countof(futexes); ++i) {
		waiters[i].val   = futexes[i];
		waiters[i].uaddr = (uintptr_t) &futexes[i];
		waiters[i].flags = FUTEX2_SIZE_U32 | FUTEX2_PRIVATE;
	}
	for (;;) {
		int              woke;
		struct timespec  timeout;
\&
		clock_gettime(CLOCK_MONOTONIC, &timeout);
		timeout.tv_sec += 1;
\&
		woke = my_futex_waitv(waiters, countof(futexes), 0, &timeout, CLOCK_MONOTONIC);
		if (woke == \-1 && (errno != EAGAIN && errno != EWOULDBLOCK))
			err(EXIT_FAILURE, "my_futex_waitv");
\&
		for (int i = 0; i < countof(futexes); ++i) {
			if (futexes[i] != waiters[i].val)
				printf("%w32u%s", futexes[i], i == woke ? "!" : "");
			putchar(\[aq]\[rs]t\[aq]);
		}
		putchar(\[aq]\[rs]n\[aq]);
\&
		for (int i = 0; i < countof(futexes); ++i)
			waiters[i].val = futexes[i];
	}
}
.EE
.\" SRC END
.SH SEE ALSO
.BR futex (2),
.BR FUTEX_WAIT (2const),
.BR FUTEX_WAKE (2const),
.BR futex (7)
.P
Kernel source file
.I Documentation/userspace-api/futex2.rst
