public inbox for linux-man@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH] futex_waitv.2: new page
@ 2026-02-07 12:49 наб
  2026-02-07 18:57 ` Alejandro Colomar
                   ` (2 more replies)
  0 siblings, 3 replies; 53+ messages in thread
From: наб @ 2026-02-07 12:49 UTC (permalink / raw)
  To: Alejandro Colomar; +Cc: linux-man

[-- Attachment #1: Type: text/plain, Size: 11545 bytes --]

Signed-off-by: Ahelenia Ziemiańska <nabijaczleweli@nabijaczleweli.xyz>
---
 man/man2/futex_waitv.2 | 406 +++++++++++++++++++++++++++++++++++++++++
 man/man7/futex.7       |   9 +-
 2 files changed, 413 insertions(+), 2 deletions(-)
 create mode 100644 man/man2/futex_waitv.2

diff --git man/man2/futex_waitv.2 man/man2/futex_waitv.2
new file mode 100644
index 000000000..2f83f098d
--- /dev/null
+++ man/man2/futex_waitv.2
@@ -0,0 +1,406 @@
+.\" Copyright, the authors of the Linux man-pages project
+.\"
+.\" SPDX-License-Identifier: MIT
+.\"
+.TH futex_waitv 2 (date) "Linux man-pages (unreleased)"
+.SH NAME
+futex_waitv \- wait for FUTEX_WAKE operation on multiple futexes
+.SH LIBRARY
+Standard C library
+.RI ( libc ,\~ \-lc )
+.SH SYNOPSIS
+.nf
+.BR "#include <linux/futex.h>" "  /* Definition of " "struct futex_waitv" " */"
+.BR "#include <sys/syscall.h>" "  /* Definition of " SYS_* " constants */"
+.B #include <unistd.h>
+.B #include <time.h>
+.P
+.BR "long syscall(" "unsigned int nr_futexes;"
+.BI "             SYS_futex_waitv, struct futex_waitv " waiters [ nr_futexes ],
+.BI "             unsigned int " nr_futexes ", unsigned int " flags ,
+.BI "             const struct timespec *_Nullable " timeout ", clockid_t " clockid ");"
+.fi
+.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)/ 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 pointed to by the addresses
+.IR waiters []. uaddr
+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 not NULL,
+.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.)
+If
+.I timeout
+is NULL, the call blocks indefinitely.
+.P
+.P
+Futex words to monitor are given by
+.P
+.in +4n
+.EX
+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
+.in
+.P
+The fields are analogous to
+.BR FUTEX_WAIT (2const)
+parameters, except
+.I __reserved
+must be 0
+and
+.I flags
+must contain one of
+.BR FUTEX2_SIZE_*
+ORed with some of the flags below.
+.P
+C programs should assign to
+.I uaddr
+by casting a pointer to
+.B uintptr_t
+to ensure the top bits are cleared on 32-bit systems.
+.TP
+.BR FUTEX2_SIZE_U32
+.I val
+and
+.I *uaddr
+are 32-bit unsigned integers.
+.TP
+.B FUTEX2_NUMA
+The futex word is followed by another word of the same size
+.RI ( uaddr
+points to
+.BR uint N _t[2]
+rather than
+.BR 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 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.
+.\" commit cec199c5e39bde7191a08087cc3d002ccfab31ff
+.\" Author: Peter Zijlstra <peterz@infradead.org>
+.\" Date:   Wed Apr 16 18:29:16 2025 +0200
+.\"
+.\"     futex: Implement FUTEX2_NUMA
+.\"
+.\" FUTEX2_MPOL is not documented or used anywhere; it's unclear to me what it does
+.\" (defined in commit c042c505210dc3453f378df432c10fff3d471bc5 "futex: Implement FUTEX2_MPOL")
+.TP
+.B FUTEX2_PRIVATE
+By default, the futex is shared
+.RB "(like " 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
+.RB "(like " FUTEX_WAIT_PRIVATE ;
+this most often means they are part of the same process).
+Private futexes are faster than shared ones.
+.\"
+.\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.\"
+.SH RETURN VALUE
+Returns an index to an
+.I 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
+is not NULL and part of the range
+.RI [ waiters ", " waiters + nr_futexes )
+is not a valid user-space address.
+.TP
+.B EFAULT
+.I timeout
+was not NULL and did not point to a valid user-space address.
+.TP
+.B EFAULT
+Any
+.IR waiters []. uaddr
+field is not a valid user-space address.
+.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
+.BR FUTEX2_SIZE_* .
+.TP
+.B EINVAL
+.I flags
+was not 0.
+.TP
+.B EINVAL
+.I nr_futexes
+was not between 1 and
+.B FUTEX_WAITV_MAX
+(128).
+.TP
+.B EINVAL
+.I timeout
+was not NULL and
+.I clockid
+was not a valid clock
+.RB ( CLOCK_MONOTONIC " or " CLOCK_REALTIME ).
+.TP
+.B EINVAL
+.I *timeout
+is denormal (before epoch or
+.I tv_nsec
+more than 999'999'999).
+.TP
+.B EINVAL
+Any
+.IR waiters []. flags
+field contains an unknown flag.
+.TP
+.B EINVAL
+Any
+.IR waiters []. flags
+field is missing a
+.B FUTEX2_SIZE_*
+flag or has a size flag different than
+.BR FUTEX2_SIZE_U32
+set.
+.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 index of the biggest NUMA domain
+(for example,
+.B FUTEX2_SIZE_U8
+and there are more than 255 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
+.I timeout
+was not NULL and no futex was woken before the timeout elapsed.
+.TP
+.B EAGAIN
+The value pointed to by
+.I uaddr
+was not equal to the expected value
+.I val
+at the time of the call.
+.IP
+.BR Note :
+on Linux, the symbolic names
+.B EAGAIN
+and
+.B EWOULDBLOCK
+(both of which appear in different parts of the kernel futex code)
+have the same value.
+.TP
+.B EINTR
+The
+operation was interrupted by a signal (see
+.BR signal (7)).
+.\"
+.\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.\"
+.SH STANDARDS
+Linux.
+.SH NOTES
+.BR FUTEX2_SIZE_U8 ", " FUTEX2_SIZE_U16 ", and " FUTEX2_SIZE_U64
+where
+.I val
+and
+.I *uaddr
+are 8, 16, or 64 bits are defined, but not implemented
+.RB ( EINVAL ).
+.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 ().
+.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!
+Connection timed out
+.EE
+.P
+.EX
+#include <linux/futex.h>
+#include <sys/syscall.h>
+#include <time.h>
+#include <unistd.h>
+\&
+#include <errno.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <stdatomic.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+\&
+void *worker(void *arg) {
+	_Atomic uint32_t *futex = arg;
+\&
+	usleep(*futex * 10000);
+	*futex *= 2;
+	syscall(SYS_futex, futex, FUTEX_WAKE_PRIVATE, 1);
+	return NULL;
+}
+\&
+int main() {
+#define WORKERS 10
+	_Atomic uint32_t futexes[WORKERS];
+\&
+	uint8_t init[WORKERS];
+	getentropy(init, sizeof(init));
+	init[0] = init[1] = init[2];
+	for(int i = 0; i < WORKERS; ++i) {
+		printf("%" PRIu8 "\\t", init[i]);
+		atomic_init(&futexes[i], init[i]);
+		pthread_create(&(pthread_t){}, NULL, worker, &futexes[i]);
+	}
+	putchar('\\n');
+\&
+	struct futex_waitv waiters[WORKERS] = {};
+	for(int i = 0; i < WORKERS; ++i) {
+		waiters[i].val   = futexes[i];
+		waiters[i].uaddr = (uintptr_t)&futexes[i];
+		waiters[i].flags = FUTEX2_SIZE_U32 | FUTEX2_PRIVATE;
+	}
+	for(struct timespec timeout;;) {
+		clock_gettime(CLOCK_MONOTONIC, &timeout);
+		timeout.tv_sec += 1;
+\&
+		int woke = syscall(SYS_futex_waitv, waiters, WORKERS, 0, &timeout, CLOCK_MONOTONIC);
+		if(woke == -1)
+			break;
+\&
+		for(int i = 0; i < WORKERS; ++i) {
+			if(futexes[i] != waiters[i].val)
+				printf("%" PRIu32 "%.*s", futexes[i], i == woke, "!");
+			putchar('\\t');
+		}
+		putchar('\\n');
+\&
+		for(int i = 0; i < WORKERS; ++i)
+			waiters[i].val = futexes[i];
+	}
+	printf("%s\\n", strerror(errno));
+}
+.EE
+.SH SEE ALSO
+.ad l
+.BR futex (2),
+.BR FUTEX_WAIT (2const),
+.BR FUTEX_WAKE (2const),
+.BR futex (7)
+.P
+The following kernel source files:
+.IP \[bu]
+.I Documentation/userspace-api/futex2.rst
+.IP \[bu]
+.I kernel/futex/syscall.c
+.IP \[bu]
+.I kernel/futex/waitwake.c
+.IP \[bu]
+.I kernel/futex/futex.h
diff --git man/man7/futex.7 man/man7/futex.7
index 51c5d5d9b..d271144ff 100644
--- man/man7/futex.7
+++ man/man7/futex.7
@@ -45,7 +45,9 @@ .SS Semantics
 Any futex operation starts in user space,
 but it may be necessary to communicate with the kernel using the
 .BR futex (2)
-system call.
+or
+.BR futex_waitv (2)
+system calls.
 .P
 To "up" a futex, execute the proper assembler instructions that
 will cause the host CPU to atomically increment the integer.
@@ -72,7 +74,9 @@ .SS Semantics
 .P
 The
 .BR futex (2)
-system call can optionally be passed a timeout specifying how long
+and
+.BR futex_waitv (2)
+system calls can optionally be passed a timeout specifying how long
 the kernel should
 wait for the futex to be upped.
 In this case, semantics are more complex and the programmer is referred
@@ -107,6 +111,7 @@ .SH NOTES
 .SH SEE ALSO
 .BR clone (2),
 .BR futex (2),
+.BR futex_waitv (2),
 .BR get_robust_list (2),
 .BR set_robust_list (2),
 .BR set_tid_address (2),
-- 
2.39.5

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* Re: [PATCH] futex_waitv.2: new page
  2026-02-07 12:49 [PATCH] futex_waitv.2: new page наб
@ 2026-02-07 18:57 ` Alejandro Colomar
  2026-02-07 19:16   ` наб
  2026-02-07 22:00 ` [PATCH v2] " наб
  2026-02-18  0:41 ` [PATCH v1 0/1] futex_waitv.2: Move text to a new PARAMETERS section Alejandro Colomar
  2 siblings, 1 reply; 53+ messages in thread
From: Alejandro Colomar @ 2026-02-07 18:57 UTC (permalink / raw)
  To: наб; +Cc: linux-man

[-- Attachment #1: Type: text/plain, Size: 14344 bytes --]

Hi наб,

On 2026-02-07T13:49:12+0100, наб wrote:
> Signed-off-by: Ahelenia Ziemiańska <nabijaczleweli@nabijaczleweli.xyz>
> ---
>  man/man2/futex_waitv.2 | 406 +++++++++++++++++++++++++++++++++++++++++
>  man/man7/futex.7       |   9 +-
>  2 files changed, 413 insertions(+), 2 deletions(-)
>  create mode 100644 man/man2/futex_waitv.2
> 
> diff --git man/man2/futex_waitv.2 man/man2/futex_waitv.2
> new file mode 100644
> index 000000000..2f83f098d
> --- /dev/null
> +++ man/man2/futex_waitv.2
> @@ -0,0 +1,406 @@
> +.\" Copyright, the authors of the Linux man-pages project
> +.\"
> +.\" SPDX-License-Identifier: MIT
> +.\"
> +.TH futex_waitv 2 (date) "Linux man-pages (unreleased)"
> +.SH NAME
> +futex_waitv \- wait for FUTEX_WAKE operation on multiple futexes
> +.SH LIBRARY
> +Standard C library
> +.RI ( libc ,\~ \-lc )
> +.SH SYNOPSIS
> +.nf
> +.BR "#include <linux/futex.h>" "  /* Definition of " "struct futex_waitv" " */"
> +.BR "#include <sys/syscall.h>" "  /* Definition of " SYS_* " constants */"
> +.B #include <unistd.h>
> +.B #include <time.h>
> +.P
> +.BR "long syscall(" "unsigned int nr_futexes;"
> +.BI "             SYS_futex_waitv, struct futex_waitv " waiters [ nr_futexes ],
> +.BI "             unsigned int " nr_futexes ", unsigned int " flags ,
> +.BI "             const struct timespec *_Nullable " timeout ", clockid_t " clockid ");"
> +.fi
> +.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)/ FUTEX_WAIT_PRIVATE

Please split this into two lines (the rule would be to never have two
identifiers on the same source line).

Also, I'd prefer English wording instead of '/'.

> +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 pointed to by the addresses
> +.IR waiters []. uaddr
> +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 not NULL,
> +.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.)

I would remove the parentheses here.  It reads fine without them.

> +If
> +.I timeout
> +is NULL, the call blocks indefinitely.
> +.P
> +.P

uniq(1)  :)

> +Futex words to monitor are given by
> +.P
> +.in +4n
> +.EX
> +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 */

Inconsistent upper-/lower-case.

Also, I'd move this to the SYNOPSIS.

> +};
> +.EE
> +.in
> +.P
> +The fields are analogous to
> +.BR FUTEX_WAIT (2const)
> +parameters, except
> +.I __reserved
> +must be 0
> +and
> +.I flags
> +must contain one of
> +.BR FUTEX2_SIZE_*
> +ORed with some of the flags below.
> +.P
> +C programs should assign to
> +.I uaddr
> +by casting a pointer to
> +.B uintptr_t
> +to ensure the top bits are cleared on 32-bit systems.
> +.TP
> +.BR FUTEX2_SIZE_U32
> +.I val
> +and
> +.I *uaddr
> +are 32-bit unsigned integers.
> +.TP
> +.B FUTEX2_NUMA
> +The futex word is followed by another word of the same size
> +.RI ( uaddr
> +points to
> +.BR uint N _t[2]

Types go in italics.

> +rather than
> +.BR 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 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.
> +.\" commit cec199c5e39bde7191a08087cc3d002ccfab31ff
> +.\" Author: Peter Zijlstra <peterz@infradead.org>
> +.\" Date:   Wed Apr 16 18:29:16 2025 +0200
> +.\"
> +.\"     futex: Implement FUTEX2_NUMA
> +.\"
> +.\" FUTEX2_MPOL is not documented or used anywhere; it's unclear to me what it does
> +.\" (defined in commit c042c505210dc3453f378df432c10fff3d471bc5 "futex: Implement FUTEX2_MPOL")
> +.TP
> +.B FUTEX2_PRIVATE
> +By default, the futex is shared
> +.RB "(like " 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

80-col right margin.  Please use semantic newlines (break at the comma).

> +.RB "(like " FUTEX_WAIT_PRIVATE ;

Please move non-formatted words to the preceeding line.

	(like
	.BR FUTEX_WAIT_PRIVATE ;

> +this most often means they are part of the same process).
> +Private futexes are faster than shared ones.
> +.\"
> +.\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
> +.\"
> +.SH RETURN VALUE
> +Returns an index to an
> +.I arbitrary

I'd say we don't need formatting in arbitrary.  The word is already an
unusual one that would put people under warning.

> +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
> +is not NULL and part of the range
> +.RI [ waiters ", " waiters + nr_futexes )

I'd format the range as

	.RI [ waiters ,
	.IR "waiters + nr_futexes" )

But I think I'd simplify as

	.I waiters
	points outside the accessible address space.

NULL is an invalid pointer, and as such, is not a valid address, so
less text to say the same.  And no need to be very specific about the
range, since the more vague wording is widely understood.

> +is not a valid user-space address.
> +.TP
> +.B EFAULT
> +.I timeout
> +was not NULL and did not point to a valid user-space address.
> +.TP
> +.B EFAULT
> +Any
> +.IR waiters []. uaddr
> +field is not a valid user-space address.
> +.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
> +.BR FUTEX2_SIZE_* .
> +.TP
> +.B EINVAL
> +.I flags
> +was not 0.
> +.TP
> +.B EINVAL
> +.I nr_futexes
> +was not between 1 and
> +.B FUTEX_WAITV_MAX
> +(128).

In this case I would like to see a range.  "Between" doesn't make it
clear whether the bounds are inclusive or exclusive or combined.

> +.TP
> +.B EINVAL
> +.I timeout
> +was not NULL and
> +.I clockid
> +was not a valid clock
> +.RB ( CLOCK_MONOTONIC " or " CLOCK_REALTIME ).

Two lines.

> +.TP
> +.B EINVAL
> +.I *timeout
> +is denormal (before epoch or
> +.I tv_nsec
> +more than 999'999'999).
> +.TP
> +.B EINVAL
> +Any
> +.IR waiters []. flags
> +field contains an unknown flag.
> +.TP
> +.B EINVAL
> +Any
> +.IR waiters []. flags
> +field is missing a
> +.B FUTEX2_SIZE_*
> +flag or has a size flag different than
> +.BR FUTEX2_SIZE_U32
> +set.
> +.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 index of the biggest NUMA domain
> +(for example,
> +.B FUTEX2_SIZE_U8
> +and there are more than 255 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
> +.I timeout
> +was not NULL and no futex was woken before the timeout elapsed.
> +.TP
> +.B EAGAIN
> +The value pointed to by
> +.I uaddr
> +was not equal to the expected value
> +.I val
> +at the time of the call.
> +.IP
> +.BR Note :
> +on Linux, the symbolic names
> +.B EAGAIN
> +and
> +.B EWOULDBLOCK
> +(both of which appear in different parts of the kernel futex code)
> +have the same value.

Should we use wording like in read(2)?  It uses

	.TP
	.BR EAGAIN " or " EWOULDBLOCK

Or do you have reasons to prefer your wording?

> +.TP
> +.B EINTR
> +The
> +operation was interrupted by a signal (see
> +.BR signal (7)).
> +.\"
> +.\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
> +.\"
> +.SH STANDARDS
> +Linux.
> +.SH NOTES

Maybe CAVEATS?

> +.BR FUTEX2_SIZE_U8 ", " FUTEX2_SIZE_U16 ", and " FUTEX2_SIZE_U64

One per line.

> +where
> +.I val
> +and
> +.I *uaddr
> +are 8, 16, or 64 bits are defined, but not implemented
> +.RB ( EINVAL ).
> +.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 ().
> +.in +4
> +.EX
> +.RB "$ " ./futex_waitv

Please use $\~ to avoid the quotes.

> +153	153	153	237	100	245	177	127	215	61
> +									122!
> +				200!
> +							254!
> +306	306!
> +		306!
> +						354!
> +								430!
> +			474!
> +					490!
> +Connection timed out
> +.EE
> +.P
> +.EX
> +#include <linux/futex.h>
> +#include <sys/syscall.h>
> +#include <time.h>
> +#include <unistd.h>
> +\&

What's the distinction between the two include groups?

> +#include <errno.h>
> +#include <inttypes.h>
> +#include <pthread.h>
> +#include <stdatomic.h>
> +#include <stdint.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +\&
> +void *worker(void *arg) {

Please format function definitions as

	T
	func(...)
	{

> +	_Atomic uint32_t *futex = arg;
> +\&
> +	usleep(*futex * 10000);
> +	*futex *= 2;
> +	syscall(SYS_futex, futex, FUTEX_WAKE_PRIVATE, 1);
> +	return NULL;
> +}
> +\&
> +int main() {

Please use the explicit void.

> +#define WORKERS 10
> +	_Atomic uint32_t futexes[WORKERS];
> +\&
> +	uint8_t init[WORKERS];
> +	getentropy(init, sizeof(init));
> +	init[0] = init[1] = init[2];
> +	for(int i = 0; i < WORKERS; ++i) {

Please use spaces after if/for/which/switch.
> +		printf("%" PRIu8 "\\t", init[i]);
> +		atomic_init(&futexes[i], init[i]);
> +		pthread_create(&(pthread_t){}, NULL, worker, &futexes[i]);
> +	}
> +	putchar('\\n');
> +\&
> +	struct futex_waitv waiters[WORKERS] = {};
> +	for(int i = 0; i < WORKERS; ++i) {
> +		waiters[i].val   = futexes[i];
> +		waiters[i].uaddr = (uintptr_t)&futexes[i];
> +		waiters[i].flags = FUTEX2_SIZE_U32 | FUTEX2_PRIVATE;
> +	}
> +	for(struct timespec timeout;;) {

Please declare within the loop:

	for (;;) {
		struct timespec  tout;


> +		clock_gettime(CLOCK_MONOTONIC, &timeout);
> +		timeout.tv_sec += 1;
> +\&
> +		int woke = syscall(SYS_futex_waitv, waiters, WORKERS, 0, &timeout, CLOCK_MONOTONIC);
> +		if(woke == -1)
> +			break;
> +\&
> +		for(int i = 0; i < WORKERS; ++i) {
> +			if(futexes[i] != waiters[i].val)
> +				printf("%" PRIu32 "%.*s", futexes[i], i == woke, "!");
> +			putchar('\\t');
> +		}
> +		putchar('\\n');
> +\&
> +		for(int i = 0; i < WORKERS; ++i)
> +			waiters[i].val = futexes[i];
> +	}
> +	printf("%s\\n", strerror(errno));
> +}
> +.EE
> +.SH SEE ALSO
> +.ad l

Unterminated .ad.  Also, do we need it?


Have a lovely night!
Alex

> +.BR futex (2),
> +.BR FUTEX_WAIT (2const),
> +.BR FUTEX_WAKE (2const),
> +.BR futex (7)
> +.P
> +The following kernel source files:
> +.IP \[bu]
> +.I Documentation/userspace-api/futex2.rst
> +.IP \[bu]
> +.I kernel/futex/syscall.c
> +.IP \[bu]
> +.I kernel/futex/waitwake.c
> +.IP \[bu]
> +.I kernel/futex/futex.h
> diff --git man/man7/futex.7 man/man7/futex.7
> index 51c5d5d9b..d271144ff 100644
> --- man/man7/futex.7
> +++ man/man7/futex.7
> @@ -45,7 +45,9 @@ .SS Semantics
>  Any futex operation starts in user space,
>  but it may be necessary to communicate with the kernel using the
>  .BR futex (2)
> -system call.
> +or
> +.BR futex_waitv (2)
> +system calls.
>  .P
>  To "up" a futex, execute the proper assembler instructions that
>  will cause the host CPU to atomically increment the integer.
> @@ -72,7 +74,9 @@ .SS Semantics
>  .P
>  The
>  .BR futex (2)
> -system call can optionally be passed a timeout specifying how long
> +and
> +.BR futex_waitv (2)
> +system calls can optionally be passed a timeout specifying how long
>  the kernel should
>  wait for the futex to be upped.
>  In this case, semantics are more complex and the programmer is referred
> @@ -107,6 +111,7 @@ .SH NOTES
>  .SH SEE ALSO
>  .BR clone (2),
>  .BR futex (2),
> +.BR futex_waitv (2),
>  .BR get_robust_list (2),
>  .BR set_robust_list (2),
>  .BR set_tid_address (2),
> -- 
> 2.39.5



-- 
<https://www.alejandro-colomar.es>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH] futex_waitv.2: new page
  2026-02-07 18:57 ` Alejandro Colomar
@ 2026-02-07 19:16   ` наб
  2026-02-07 21:50     ` Alejandro Colomar
  0 siblings, 1 reply; 53+ messages in thread
From: наб @ 2026-02-07 19:16 UTC (permalink / raw)
  To: Alejandro Colomar; +Cc: linux-man

[-- Attachment #1: Type: text/plain, Size: 2325 bytes --]

Hi!

On Sat, Feb 07, 2026 at 07:57:03PM +0100, Alejandro Colomar wrote:
> On 2026-02-07T13:49:12+0100, наб wrote:
> > +.TP
> > +.B EINVAL
> > +.I nr_futexes
> > +was not between 1 and
> > +.B FUTEX_WAITV_MAX
> > +(128).
> 
> In this case I would like to see a range.  "Between" doesn't make it
> clear whether the bounds are inclusive or exclusive or combined.

"nr_futexes was not in [1, FUTEX_WAITV_MAX (128)]."?

> > +.TP
> > +.B EAGAIN
> > +The value pointed to by
> > +.I uaddr
> > +was not equal to the expected value
> > +.I val
> > +at the time of the call.
> > +.IP
> > +.BR Note :
> > +on Linux, the symbolic names
> > +.B EAGAIN
> > +and
> > +.B EWOULDBLOCK
> > +(both of which appear in different parts of the kernel futex code)
> > +have the same value.
> 
> Should we use wording like in read(2)?  It uses
> 
> 	.TP
> 	.BR EAGAIN " or " EWOULDBLOCK
> 
> Or do you have reasons to prefer your wording?

That's what FUTEX_WAIT(2const) says and that's the most-sister page
I copied this from (it is pretty crazy wording). But
  $ git grep 'hich appear in different'
  man/man2/futex_waitv.2:(both of which appear in different parts of the kernel futex code)
  man/man2const/FUTEX_WAIT.2const:(both of which appear in different parts of the kernel futex code)
  man/man2const/FUTEX_WAIT_BITSET.2const:(both of which appear in different parts of the kernel futex code)
  man/man2const/FUTEX_WAIT_REQUEUE_PI.2const:(both of which appear in different parts of the kernel futex code)
so those want to get those gone as well.

> > +.SH NOTES
> 
> Maybe CAVEATS?

idk if it's a caveat... the futex(2) API family functionally hard-codes
FUTEX2_SIZE_U32 into every operation and it's not a caveat there.
And, having just seen a lot of futex kernel code, it abuses futexes
being 4 bytes /so much/. I don't think anyone's expecting to have
non-4-byte futexes any time soon. So this is more like a notable curio
to explain why you need FUTEX2_SIZE_U32 at all than "beware this missing API".
Y/N?

> > +#include <linux/futex.h>
> > +#include <sys/syscall.h>
> > +#include <time.h>
> > +#include <unistd.h>
> > +\&
> 
> What's the distinction between the two include groups?
Top 4 are for the syscall from SYNOPSIS,
rest are for application code.

Best,

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH] futex_waitv.2: new page
  2026-02-07 19:16   ` наб
@ 2026-02-07 21:50     ` Alejandro Colomar
  0 siblings, 0 replies; 53+ messages in thread
From: Alejandro Colomar @ 2026-02-07 21:50 UTC (permalink / raw)
  To: наб; +Cc: linux-man

[-- Attachment #1: Type: text/plain, Size: 2917 bytes --]

On 2026-02-07T20:16:36+0100, наб wrote:
> Hi!

Hi!

> 
> On Sat, Feb 07, 2026 at 07:57:03PM +0100, Alejandro Colomar wrote:
> > On 2026-02-07T13:49:12+0100, наб wrote:
> > > +.TP
> > > +.B EINVAL
> > > +.I nr_futexes
> > > +was not between 1 and
> > > +.B FUTEX_WAITV_MAX
> > > +(128).
> > 
> > In this case I would like to see a range.  "Between" doesn't make it
> > clear whether the bounds are inclusive or exclusive or combined.
> 
> "nr_futexes was not in [1, FUTEX_WAITV_MAX (128)]."?

Yeah, that sounds quite good.

> > > +.TP
> > > +.B EAGAIN
> > > +The value pointed to by
> > > +.I uaddr
> > > +was not equal to the expected value
> > > +.I val
> > > +at the time of the call.
> > > +.IP
> > > +.BR Note :
> > > +on Linux, the symbolic names
> > > +.B EAGAIN
> > > +and
> > > +.B EWOULDBLOCK
> > > +(both of which appear in different parts of the kernel futex code)
> > > +have the same value.
> > 
> > Should we use wording like in read(2)?  It uses
> > 
> > 	.TP
> > 	.BR EAGAIN " or " EWOULDBLOCK
> > 
> > Or do you have reasons to prefer your wording?
> 
> That's what FUTEX_WAIT(2const) says and that's the most-sister page
> I copied this from (it is pretty crazy wording). But

Makes sense, thanks!

>   $ git grep 'hich appear in different'
>   man/man2/futex_waitv.2:(both of which appear in different parts of the kernel futex code)
>   man/man2const/FUTEX_WAIT.2const:(both of which appear in different parts of the kernel futex code)
>   man/man2const/FUTEX_WAIT_BITSET.2const:(both of which appear in different parts of the kernel futex code)
>   man/man2const/FUTEX_WAIT_REQUEUE_PI.2const:(both of which appear in different parts of the kernel futex code)
> so those want to get those gone as well.

Yeah, if you feel like cleaning up the wording in all of them, that
would help.  On the other hand, I would understand if you don't feel
like doing that.  Do what you prefer.  :)

> 
> > > +.SH NOTES
> > 
> > Maybe CAVEATS?
> 
> idk if it's a caveat... the futex(2) API family functionally hard-codes
> FUTEX2_SIZE_U32 into every operation and it's not a caveat there.
> And, having just seen a lot of futex kernel code, it abuses futexes
> being 4 bytes /so much/. I don't think anyone's expecting to have
> non-4-byte futexes any time soon. So this is more like a notable curio
> to explain why you need FUTEX2_SIZE_U32 at all than "beware this missing API".
> Y/N?

Agree.

> 
> > > +#include <linux/futex.h>
> > > +#include <sys/syscall.h>
> > > +#include <time.h>
> > > +#include <unistd.h>
> > > +\&
> > 
> > What's the distinction between the two include groups?
> Top 4 are for the syscall from SYNOPSIS,
> rest are for application code.

I would merge them.  After all, we already have the SYNOPSIS for that.


Cheers,
Alex

> 
> Best,



-- 
<https://www.alejandro-colomar.es>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [PATCH v2] futex_waitv.2: new page
  2026-02-07 12:49 [PATCH] futex_waitv.2: new page наб
  2026-02-07 18:57 ` Alejandro Colomar
@ 2026-02-07 22:00 ` наб
  2026-02-09 22:35   ` Alejandro Colomar
  2026-02-10 20:05   ` Alejandro Colomar
  2026-02-18  0:41 ` [PATCH v1 0/1] futex_waitv.2: Move text to a new PARAMETERS section Alejandro Colomar
  2 siblings, 2 replies; 53+ messages in thread
From: наб @ 2026-02-07 22:00 UTC (permalink / raw)
  To: Alejandro Colomar; +Cc: linux-man

[-- Attachment #1: Type: text/plain, Size: 11363 bytes --]

Signed-off-by: Ahelenia Ziemiańska <nabijaczleweli@nabijaczleweli.xyz>
---
 man/man2/futex_waitv.2 | 407 +++++++++++++++++++++++++++++++++++++++++
 man/man7/futex.7       |   9 +-
 2 files changed, 414 insertions(+), 2 deletions(-)
 create mode 100644 man/man2/futex_waitv.2

diff --git man/man2/futex_waitv.2 man/man2/futex_waitv.2
new file mode 100644
index 000000000..2645f4147
--- /dev/null
+++ man/man2/futex_waitv.2
@@ -0,0 +1,407 @@
+.\" Copyright, the authors of the Linux man-pages project
+.\"
+.\" SPDX-License-Identifier: MIT
+.\"
+.TH futex_waitv 2 (date) "Linux man-pages (unreleased)"
+.SH NAME
+futex_waitv \- wait for FUTEX_WAKE operation on multiple futexes
+.SH LIBRARY
+Standard C library
+.RI ( libc ,\~ \-lc )
+.SH SYNOPSIS
+.nf
+.BR "#include <linux/futex.h>" "  /* Definition of " "struct futex_waitv" " */"
+.BR "#include <sys/syscall.h>" "  /* Definition of " SYS_* " constants */"
+.B #include <unistd.h>
+.B #include <time.h>
+.P
+.BR "long syscall(" "unsigned int nr_futexes;"
+.BI "             SYS_futex_waitv, struct futex_waitv " waiters [ nr_futexes ],
+.BI "             unsigned int " nr_futexes ", unsigned int " flags ,
+.BI "             const struct timespec *_Nullable " timeout ", clockid_t " clockid ");"
+.fi
+.P
+.EX
+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 pointed to by the addresses
+.IR waiters []. uaddr
+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 not NULL,
+.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.
+If
+.I timeout
+is NULL, the call blocks indefinitely.
+.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 one of
+.BR FUTEX2_SIZE_*
+ORed with some of the flags below.
+.P
+C programs should assign to
+.I uaddr
+by casting a pointer to
+.B uintptr_t
+to ensure the top bits are cleared on 32-bit systems.
+.TP
+.BR FUTEX2_SIZE_U32
+.I val
+and
+.I *uaddr
+are 32-bit unsigned integers.
+.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 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.
+.\" commit cec199c5e39bde7191a08087cc3d002ccfab31ff
+.\" Author: Peter Zijlstra <peterz@infradead.org>
+.\" Date:   Wed Apr 16 18:29:16 2025 +0200
+.\"
+.\"     futex: Implement FUTEX2_NUMA
+.\"
+.\" FUTEX2_MPOL is not documented or used anywhere;
+.\" it's unclear to me what it does
+.\" (defined in commit c042c505210dc3453f378df432c10fff3d471bc5
+.\"  "futex: Implement FUTEX2_MPOL")
+.TP
+.B FUTEX2_PRIVATE
+By default, the futex is shared
+.RB "(like " 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.
+.\"
+.\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.\"
+.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
+was not NULL and did not point to a valid user-space address.
+.TP
+.B EFAULT
+Any
+.IR waiters []. uaddr
+field is not a valid user-space address.
+.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
+.BR FUTEX2_SIZE_* .
+.TP
+.B EINVAL
+.I flags
+was not 0.
+.TP
+.B EINVAL
+.I nr_futexes
+was not in
+[1,
+.B FUTEX_WAITV_MAX
+(128)].
+.TP
+.B EINVAL
+.I timeout
+was not NULL and
+.I clockid
+was not a valid clock
+.RB ( CLOCK_MONOTONIC
+or
+.BR CLOCK_REALTIME ).
+.TP
+.B EINVAL
+.I *timeout
+is denormal (before epoch or
+.I tv_nsec
+more than 999'999'999).
+.TP
+.B EINVAL
+Any
+.IR waiters []. flags
+field contains an unknown flag.
+.TP
+.B EINVAL
+Any
+.IR waiters []. flags
+field is missing a
+.B FUTEX2_SIZE_*
+flag or has a size flag different than
+.BR FUTEX2_SIZE_U32
+set.
+.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 index of the biggest NUMA domain
+(for example,
+.B FUTEX2_SIZE_U8
+and there are more than 255 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
+.I timeout
+was not NULL and no futex was woken before the timeout elapsed.
+.TP
+.BR EAGAIN " or " EWOULDBLOCK
+The value pointed to by
+.I uaddr
+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 NOTES
+.BR FUTEX2_SIZE_U8 ,
+.BR FUTEX2_SIZE_U16 ,
+and
+.B FUTEX2_SIZE_U64
+where
+.I val
+and
+.I *uaddr
+are 8, 16, or 64 bits are defined, but not implemented
+.RB ( EINVAL ).
+.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 ().
+.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!
+Connection timed out
+.EE
+.P
+.EX
+#include <errno.h>
+#include <inttypes.h>
+#include <linux/futex.h>
+#include <pthread.h>
+#include <stdatomic.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/syscall.h>
+#include <time.h>
+#include <unistd.h>
+\&
+void *
+worker(void *arg)
+{
+	_Atomic uint32_t *futex = arg;
+\&
+	usleep(*futex * 10000);
+	*futex *= 2;
+	syscall(SYS_futex, futex, FUTEX_WAKE_PRIVATE, 1);
+	return NULL;
+}
+\&
+int
+main(void)
+{
+#define WORKERS 10
+	_Atomic uint32_t futexes[WORKERS];
+\&
+	uint8_t init[WORKERS];
+	getentropy(init, sizeof(init));
+	init[0] = init[1] = init[2];
+	for (int i = 0; i < WORKERS; ++i) {
+		printf("%" PRIu8 "\\t", init[i]);
+		atomic_init(&futexes[i], init[i]);
+		pthread_create(&(pthread_t){}, NULL, worker, &futexes[i]);
+	}
+	putchar('\\n');
+\&
+	struct futex_waitv waiters[WORKERS] = {};
+	for (int i = 0; i < WORKERS; ++i) {
+		waiters[i].val   = futexes[i];
+		waiters[i].uaddr = (uintptr_t)&futexes[i];
+		waiters[i].flags = FUTEX2_SIZE_U32 | FUTEX2_PRIVATE;
+	}
+	for (;;) {
+		struct timespec timeout
+		clock_gettime(CLOCK_MONOTONIC, &timeout);
+		timeout.tv_sec += 1;
+\&
+		int woke = syscall(SYS_futex_waitv, waiters, WORKERS, 0, &timeout, CLOCK_MONOTONIC);
+		if (woke == -1)
+			break;
+\&
+		for (int i = 0; i < WORKERS; ++i) {
+			if (futexes[i] != waiters[i].val)
+				printf("%" PRIu32 "%.*s", futexes[i], i == woke, "!");
+			putchar('\\t');
+		}
+		putchar('\\n');
+\&
+		for (int i = 0; i < WORKERS; ++i)
+			waiters[i].val = futexes[i];
+	}
+	printf("%s\\n", strerror(errno));
+}
+.EE
+.SH SEE ALSO
+.ad l
+.BR futex (2),
+.BR FUTEX_WAIT (2const),
+.BR FUTEX_WAKE (2const),
+.BR futex (7)
+.P
+The following kernel source files:
+.IP \[bu]
+.I Documentation/userspace-api/futex2.rst
+.IP \[bu]
+.I kernel/futex/syscall.c
+.IP \[bu]
+.I kernel/futex/waitwake.c
+.IP \[bu]
+.I kernel/futex/futex.h
diff --git man/man7/futex.7 man/man7/futex.7
index 51c5d5d9b..d271144ff 100644
--- man/man7/futex.7
+++ man/man7/futex.7
@@ -45,7 +45,9 @@ .SS Semantics
 Any futex operation starts in user space,
 but it may be necessary to communicate with the kernel using the
 .BR futex (2)
-system call.
+or
+.BR futex_waitv (2)
+system calls.
 .P
 To "up" a futex, execute the proper assembler instructions that
 will cause the host CPU to atomically increment the integer.
@@ -72,7 +74,9 @@ .SS Semantics
 .P
 The
 .BR futex (2)
-system call can optionally be passed a timeout specifying how long
+and
+.BR futex_waitv (2)
+system calls can optionally be passed a timeout specifying how long
 the kernel should
 wait for the futex to be upped.
 In this case, semantics are more complex and the programmer is referred
@@ -107,6 +111,7 @@ .SH NOTES
 .SH SEE ALSO
 .BR clone (2),
 .BR futex (2),
+.BR futex_waitv (2),
 .BR get_robust_list (2),
 .BR set_robust_list (2),
 .BR set_tid_address (2),
-- 
2.39.5

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* Re: [PATCH v2] futex_waitv.2: new page
  2026-02-07 22:00 ` [PATCH v2] " наб
@ 2026-02-09 22:35   ` Alejandro Colomar
  2026-02-10 14:17     ` наб
  2026-02-10 20:05   ` Alejandro Colomar
  1 sibling, 1 reply; 53+ messages in thread
From: Alejandro Colomar @ 2026-02-09 22:35 UTC (permalink / raw)
  To: наб; +Cc: linux-man

[-- Attachment #1: Type: text/plain, Size: 13273 bytes --]

Hi!

On 2026-02-07T23:00:49+0100, наб wrote:
> Signed-off-by: Ahelenia Ziemiańska <nabijaczleweli@nabijaczleweli.xyz>

For some reason, the patch doesn't want to apply.  I don't see anything
obviously wrong, so it may be an issue in my side?

	Applying: futex_waitv.2: new page
	error: affected file 'man2/futex_waitv.2' is beyond a symbolic link
	error: man7/futex.7: does not exist in index
	Patch failed at 0001 futex_waitv.2: new page
	hint: Use 'git am --show-current-patch=diff' to see the failed patch
	hint: When you have resolved this problem, run "git am --continue".
	hint: If you prefer to skip this patch, run "git am --skip" instead.
	hint: To restore the original branch and stop patching, run "git am --abort".
	hint: Disable this message with "git config set advice.mergeConflict false"
	Press any key to continue...

The patch uses the correct paths without any symlinks.  Maybe my git(1)
is somehow broken?

	$ git --version
	git version 2.51.0


Have a lovely night!
Alex

> ---
>  man/man2/futex_waitv.2 | 407 +++++++++++++++++++++++++++++++++++++++++
>  man/man7/futex.7       |   9 +-
>  2 files changed, 414 insertions(+), 2 deletions(-)
>  create mode 100644 man/man2/futex_waitv.2
> 
> diff --git man/man2/futex_waitv.2 man/man2/futex_waitv.2
> new file mode 100644
> index 000000000..2645f4147
> --- /dev/null
> +++ man/man2/futex_waitv.2
> @@ -0,0 +1,407 @@
> +.\" Copyright, the authors of the Linux man-pages project
> +.\"
> +.\" SPDX-License-Identifier: MIT
> +.\"
> +.TH futex_waitv 2 (date) "Linux man-pages (unreleased)"
> +.SH NAME
> +futex_waitv \- wait for FUTEX_WAKE operation on multiple futexes
> +.SH LIBRARY
> +Standard C library
> +.RI ( libc ,\~ \-lc )
> +.SH SYNOPSIS
> +.nf
> +.BR "#include <linux/futex.h>" "  /* Definition of " "struct futex_waitv" " */"
> +.BR "#include <sys/syscall.h>" "  /* Definition of " SYS_* " constants */"
> +.B #include <unistd.h>
> +.B #include <time.h>
> +.P
> +.BR "long syscall(" "unsigned int nr_futexes;"
> +.BI "             SYS_futex_waitv, struct futex_waitv " waiters [ nr_futexes ],
> +.BI "             unsigned int " nr_futexes ", unsigned int " flags ,
> +.BI "             const struct timespec *_Nullable " timeout ", clockid_t " clockid ");"
> +.fi
> +.P
> +.EX
> +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 pointed to by the addresses
> +.IR waiters []. uaddr
> +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 not NULL,
> +.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.
> +If
> +.I timeout
> +is NULL, the call blocks indefinitely.
> +.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 one of
> +.BR FUTEX2_SIZE_*
> +ORed with some of the flags below.
> +.P
> +C programs should assign to
> +.I uaddr
> +by casting a pointer to
> +.B uintptr_t
> +to ensure the top bits are cleared on 32-bit systems.
> +.TP
> +.BR FUTEX2_SIZE_U32
> +.I val
> +and
> +.I *uaddr
> +are 32-bit unsigned integers.
> +.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 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.
> +.\" commit cec199c5e39bde7191a08087cc3d002ccfab31ff
> +.\" Author: Peter Zijlstra <peterz@infradead.org>
> +.\" Date:   Wed Apr 16 18:29:16 2025 +0200
> +.\"
> +.\"     futex: Implement FUTEX2_NUMA
> +.\"
> +.\" FUTEX2_MPOL is not documented or used anywhere;
> +.\" it's unclear to me what it does
> +.\" (defined in commit c042c505210dc3453f378df432c10fff3d471bc5
> +.\"  "futex: Implement FUTEX2_MPOL")
> +.TP
> +.B FUTEX2_PRIVATE
> +By default, the futex is shared
> +.RB "(like " 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.
> +.\"
> +.\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
> +.\"
> +.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
> +was not NULL and did not point to a valid user-space address.
> +.TP
> +.B EFAULT
> +Any
> +.IR waiters []. uaddr
> +field is not a valid user-space address.
> +.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
> +.BR FUTEX2_SIZE_* .
> +.TP
> +.B EINVAL
> +.I flags
> +was not 0.
> +.TP
> +.B EINVAL
> +.I nr_futexes
> +was not in
> +[1,
> +.B FUTEX_WAITV_MAX
> +(128)].
> +.TP
> +.B EINVAL
> +.I timeout
> +was not NULL and
> +.I clockid
> +was not a valid clock
> +.RB ( CLOCK_MONOTONIC
> +or
> +.BR CLOCK_REALTIME ).
> +.TP
> +.B EINVAL
> +.I *timeout
> +is denormal (before epoch or
> +.I tv_nsec
> +more than 999'999'999).
> +.TP
> +.B EINVAL
> +Any
> +.IR waiters []. flags
> +field contains an unknown flag.
> +.TP
> +.B EINVAL
> +Any
> +.IR waiters []. flags
> +field is missing a
> +.B FUTEX2_SIZE_*
> +flag or has a size flag different than
> +.BR FUTEX2_SIZE_U32
> +set.
> +.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 index of the biggest NUMA domain
> +(for example,
> +.B FUTEX2_SIZE_U8
> +and there are more than 255 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
> +.I timeout
> +was not NULL and no futex was woken before the timeout elapsed.
> +.TP
> +.BR EAGAIN " or " EWOULDBLOCK
> +The value pointed to by
> +.I uaddr
> +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 NOTES
> +.BR FUTEX2_SIZE_U8 ,
> +.BR FUTEX2_SIZE_U16 ,
> +and
> +.B FUTEX2_SIZE_U64
> +where
> +.I val
> +and
> +.I *uaddr
> +are 8, 16, or 64 bits are defined, but not implemented
> +.RB ( EINVAL ).
> +.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 ().
> +.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!
> +Connection timed out
> +.EE
> +.P
> +.EX
> +#include <errno.h>
> +#include <inttypes.h>
> +#include <linux/futex.h>
> +#include <pthread.h>
> +#include <stdatomic.h>
> +#include <stdint.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <sys/syscall.h>
> +#include <time.h>
> +#include <unistd.h>
> +\&
> +void *
> +worker(void *arg)
> +{
> +	_Atomic uint32_t *futex = arg;
> +\&
> +	usleep(*futex * 10000);
> +	*futex *= 2;
> +	syscall(SYS_futex, futex, FUTEX_WAKE_PRIVATE, 1);
> +	return NULL;
> +}
> +\&
> +int
> +main(void)
> +{
> +#define WORKERS 10
> +	_Atomic uint32_t futexes[WORKERS];
> +\&
> +	uint8_t init[WORKERS];
> +	getentropy(init, sizeof(init));
> +	init[0] = init[1] = init[2];
> +	for (int i = 0; i < WORKERS; ++i) {
> +		printf("%" PRIu8 "\\t", init[i]);
> +		atomic_init(&futexes[i], init[i]);
> +		pthread_create(&(pthread_t){}, NULL, worker, &futexes[i]);
> +	}
> +	putchar('\\n');
> +\&
> +	struct futex_waitv waiters[WORKERS] = {};
> +	for (int i = 0; i < WORKERS; ++i) {
> +		waiters[i].val   = futexes[i];
> +		waiters[i].uaddr = (uintptr_t)&futexes[i];
> +		waiters[i].flags = FUTEX2_SIZE_U32 | FUTEX2_PRIVATE;
> +	}
> +	for (;;) {
> +		struct timespec timeout
> +		clock_gettime(CLOCK_MONOTONIC, &timeout);
> +		timeout.tv_sec += 1;
> +\&
> +		int woke = syscall(SYS_futex_waitv, waiters, WORKERS, 0, &timeout, CLOCK_MONOTONIC);
> +		if (woke == -1)
> +			break;
> +\&
> +		for (int i = 0; i < WORKERS; ++i) {
> +			if (futexes[i] != waiters[i].val)
> +				printf("%" PRIu32 "%.*s", futexes[i], i == woke, "!");
> +			putchar('\\t');
> +		}
> +		putchar('\\n');
> +\&
> +		for (int i = 0; i < WORKERS; ++i)
> +			waiters[i].val = futexes[i];
> +	}
> +	printf("%s\\n", strerror(errno));
> +}
> +.EE
> +.SH SEE ALSO
> +.ad l
> +.BR futex (2),
> +.BR FUTEX_WAIT (2const),
> +.BR FUTEX_WAKE (2const),
> +.BR futex (7)
> +.P
> +The following kernel source files:
> +.IP \[bu]
> +.I Documentation/userspace-api/futex2.rst
> +.IP \[bu]
> +.I kernel/futex/syscall.c
> +.IP \[bu]
> +.I kernel/futex/waitwake.c
> +.IP \[bu]
> +.I kernel/futex/futex.h
> diff --git man/man7/futex.7 man/man7/futex.7
> index 51c5d5d9b..d271144ff 100644
> --- man/man7/futex.7
> +++ man/man7/futex.7
> @@ -45,7 +45,9 @@ .SS Semantics
>  Any futex operation starts in user space,
>  but it may be necessary to communicate with the kernel using the
>  .BR futex (2)
> -system call.
> +or
> +.BR futex_waitv (2)
> +system calls.
>  .P
>  To "up" a futex, execute the proper assembler instructions that
>  will cause the host CPU to atomically increment the integer.
> @@ -72,7 +74,9 @@ .SS Semantics
>  .P
>  The
>  .BR futex (2)
> -system call can optionally be passed a timeout specifying how long
> +and
> +.BR futex_waitv (2)
> +system calls can optionally be passed a timeout specifying how long
>  the kernel should
>  wait for the futex to be upped.
>  In this case, semantics are more complex and the programmer is referred
> @@ -107,6 +111,7 @@ .SH NOTES
>  .SH SEE ALSO
>  .BR clone (2),
>  .BR futex (2),
> +.BR futex_waitv (2),
>  .BR get_robust_list (2),
>  .BR set_robust_list (2),
>  .BR set_tid_address (2),
> -- 
> 2.39.5



-- 
<https://www.alejandro-colomar.es>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v2] futex_waitv.2: new page
  2026-02-09 22:35   ` Alejandro Colomar
@ 2026-02-10 14:17     ` наб
  2026-02-10 14:30       ` Alejandro Colomar
  0 siblings, 1 reply; 53+ messages in thread
From: наб @ 2026-02-10 14:17 UTC (permalink / raw)
  To: Alejandro Colomar; +Cc: linux-man

[-- Attachment #1: Type: text/plain, Size: 1618 bytes --]

Hi!

On Mon, Feb 09, 2026 at 11:35:53PM +0100, Alejandro Colomar wrote:
> On 2026-02-07T23:00:49+0100, наб wrote:
> > Signed-off-by: Ahelenia Ziemiańska <nabijaczleweli@nabijaczleweli.xyz>
> 
> For some reason, the patch doesn't want to apply.  I don't see anything
> obviously wrong, so it may be an issue in my side?
> 
> 	Applying: futex_waitv.2: new page
> 	error: affected file 'man2/futex_waitv.2' is beyond a symbolic link
> 	error: man7/futex.7: does not exist in index
> 	Patch failed at 0001 futex_waitv.2: new page
> 	hint: Use 'git am --show-current-patch=diff' to see the failed patch
> 	hint: When you have resolved this problem, run "git am --continue".
> 	hint: If you prefer to skip this patch, run "git am --skip" instead.
> 	hint: To restore the original branch and stop patching, run "git am --abort".
> 	hint: Disable this message with "git config set advice.mergeConflict false"
> 	Press any key to continue...

Hm, I did recently set
  $ git config diff.noprefix
  true
I didn't expect this to affect format-patch diffs
(since it doesn't affect diffs shown by git add -p),
or, if it did, I expected the designated consumer of format-patch
diffs (am) to understand this. perhaps not;
maybe -p0 to git am?

A quick search yields
  https://lore.kernel.org/git/xmqqy1o5op1i.fsf@gitster.g/t/#eaa0323ec4eed441b37caf96e1b136529b298dbac
where you're in the thread and the maintainer says "queued" for a patch
that would ignore noprefix for format-patch for this reason precisely.
But clearly not, since my patches were with noprefix=true and came out -p0.

Best,

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v2] futex_waitv.2: new page
  2026-02-10 14:17     ` наб
@ 2026-02-10 14:30       ` Alejandro Colomar
  2026-02-10 15:54         ` Kristoffer Haugsbakk
  2026-02-10 16:54         ` Junio C Hamano
  0 siblings, 2 replies; 53+ messages in thread
From: Alejandro Colomar @ 2026-02-10 14:30 UTC (permalink / raw)
  To: git, наб; +Cc: linux-man

[-- Attachment #1: Type: text/plain, Size: 1989 bytes --]

Hi!

On 2026-02-10T15:17:55+0100, наб wrote:
> Hi!
> 
> On Mon, Feb 09, 2026 at 11:35:53PM +0100, Alejandro Colomar wrote:
> > On 2026-02-07T23:00:49+0100, наб wrote:
> > > Signed-off-by: Ahelenia Ziemiańska <nabijaczleweli@nabijaczleweli.xyz>
> > 
> > For some reason, the patch doesn't want to apply.  I don't see anything
> > obviously wrong, so it may be an issue in my side?
> > 
> > 	Applying: futex_waitv.2: new page
> > 	error: affected file 'man2/futex_waitv.2' is beyond a symbolic link
> > 	error: man7/futex.7: does not exist in index
> > 	Patch failed at 0001 futex_waitv.2: new page
> > 	hint: Use 'git am --show-current-patch=diff' to see the failed patch
> > 	hint: When you have resolved this problem, run "git am --continue".
> > 	hint: If you prefer to skip this patch, run "git am --skip" instead.
> > 	hint: To restore the original branch and stop patching, run "git am --abort".
> > 	hint: Disable this message with "git config set advice.mergeConflict false"
> > 	Press any key to continue...
> 
> Hm, I did recently set
>   $ git config diff.noprefix
>   true
> I didn't expect this to affect format-patch diffs
> (since it doesn't affect diffs shown by git add -p),
> or, if it did, I expected the designated consumer of format-patch
> diffs (am) to understand this. perhaps not;
> maybe -p0 to git am?
> 
> A quick search yields
>   https://lore.kernel.org/git/xmqqy1o5op1i.fsf@gitster.g/t/#eaa0323ec4eed441b37caf96e1b136529b298dbac
> where you're in the thread and the maintainer says "queued" for a patch
> that would ignore noprefix for format-patch for this reason precisely.
> But clearly not, since my patches were with noprefix=true and came out -p0.

Oh, that bites again!

Junio, do you still have this queued?
<https://lore.kernel.org/git/xmqqy1o5op1i.fsf@gitster.g/t/#m6f42ff4f0cb2d6dd1d68f12a533d04c822b68a80>


Have a lovely day!
Alex

> 
> Best,



-- 
<https://www.alejandro-colomar.es>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v2] futex_waitv.2: new page
  2026-02-10 14:30       ` Alejandro Colomar
@ 2026-02-10 15:54         ` Kristoffer Haugsbakk
  2026-02-10 18:39           ` Alejandro Colomar
  2026-02-11  7:35           ` Jeff King
  2026-02-10 16:54         ` Junio C Hamano
  1 sibling, 2 replies; 53+ messages in thread
From: Kristoffer Haugsbakk @ 2026-02-10 15:54 UTC (permalink / raw)
  To: Alejandro Colomar, git, наб; +Cc: linux-man, Jeff King

On Tue, Feb 10, 2026, at 15:30, Alejandro Colomar wrote:
> Hi!
>
> On 2026-02-10T15:17:55+0100, наб wrote:
>> Hi!
>>
>> On Mon, Feb 09, 2026 at 11:35:53PM +0100, Alejandro Colomar wrote:
>> > On 2026-02-07T23:00:49+0100, наб wrote:
>> > > Signed-off-by: Ahelenia Ziemiańska <nabijaczleweli@nabijaczleweli.xyz>
>> >
>> > For some reason, the patch doesn't want to apply.  I don't see anything
>> > obviously wrong, so it may be an issue in my side?
>> >
>> > 	Applying: futex_waitv.2: new page
>> > 	error: affected file 'man2/futex_waitv.2' is beyond a symbolic link
>> > 	error: man7/futex.7: does not exist in index
>> > 	Patch failed at 0001 futex_waitv.2: new page
>> > 	hint: Use 'git am --show-current-patch=diff' to see the failed patch
>> > 	hint: When you have resolved this problem, run "git am --continue".
>> > 	hint: If you prefer to skip this patch, run "git am --skip" instead.
>> > 	hint: To restore the original branch and stop patching, run "git am --abort".
>> > 	hint: Disable this message with "git config set advice.mergeConflict false"
>> > 	Press any key to continue...
>>
>> Hm, I did recently set
>>   $ git config diff.noprefix
>>   true
>> I didn't expect this to affect format-patch diffs
>> (since it doesn't affect diffs shown by git add -p),
>> or, if it did, I expected the designated consumer of format-patch
>> diffs (am) to understand this. perhaps not;
>> maybe -p0 to git am?
>>
>> A quick search yields
>>   https://lore.kernel.org/git/xmqqy1o5op1i.fsf@gitster.g/t/#eaa0323ec4eed441b37caf96e1b136529b298dbac
>> where you're in the thread and the maintainer says "queued" for a patch
>> that would ignore noprefix for format-patch for this reason precisely.
>> But clearly not, since my patches were with noprefix=true and came out -p0.
>
> Oh, that bites again!
>
> Junio, do you still have this queued?
> <https://lore.kernel.org/git/xmqqy1o5op1i.fsf@gitster.g/t/#m6f42ff4f0cb2d6dd1d68f12a533d04c822b68a80>

See 15108de2 (Merge branch 'jk/format-patch-ignore-noprefix',
2023-03-21).

An aside but `format.noprefix` is not a boolean like `diff.noprefix`.
It will be enabled with any value.

Is it standard to indicate this with the existing “If set,”, perhaps? Or
should it say “enabled with any value”?

(+Cc Peff)

    format.noprefix::
            If set, do not show any source or destination prefix in patches.
            This is equivalent to the `diff.noprefix` option used by `git
            diff` (but which is not respected by `format-patch`). Note that
            by setting this, the receiver of any patches you generate will
            have to apply them using the `-p0` option.

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v2] futex_waitv.2: new page
  2026-02-10 14:30       ` Alejandro Colomar
  2026-02-10 15:54         ` Kristoffer Haugsbakk
@ 2026-02-10 16:54         ` Junio C Hamano
  2026-02-10 17:11           ` Kristoffer Haugsbakk
  2026-02-10 18:44           ` Alejandro Colomar
  1 sibling, 2 replies; 53+ messages in thread
From: Junio C Hamano @ 2026-02-10 16:54 UTC (permalink / raw)
  To: Alejandro Colomar; +Cc: git, наб, linux-man

Alejandro Colomar <alx@kernel.org> writes:

> Junio, do you still have this queued?
> <https://lore.kernel.org/git/xmqqy1o5op1i.fsf@gitster.g/t/#m6f42ff4f0cb2d6dd1d68f12a533d04c822b68a80>

Still queued??  Not very likely.

It is a topic from almost 3 years ago, so it is either in an ancient
released version, or it was discarded long time ago for some issues.
Given that it is from Peff, it is very likely the former.

https://git.kernel.org/pub/scm/git/git.git/commit/?h=15108de2fa0&id=15108de2fa0cd8f002a0551d14c84505a853071c    

That's v2.41.0-rc0~141 if my "git describe --contains" is counting
correctly.

My secretary will bill you for 30 minutes of my time and for making
me miss a meeting with external folks with this ;-).

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v2] futex_waitv.2: new page
  2026-02-10 16:54         ` Junio C Hamano
@ 2026-02-10 17:11           ` Kristoffer Haugsbakk
  2026-02-10 18:44           ` Alejandro Colomar
  1 sibling, 0 replies; 53+ messages in thread
From: Kristoffer Haugsbakk @ 2026-02-10 17:11 UTC (permalink / raw)
  To: Junio C Hamano, Alejandro Colomar; +Cc: git, наб, linux-man

On Tue, Feb 10, 2026, at 17:54, Junio C Hamano wrote:
> Alejandro Colomar <alx@kernel.org> writes:
>
>> Junio, do you still have this queued?
>> <https://lore.kernel.org/git/xmqqy1o5op1i.fsf@gitster.g/t/#m6f42ff4f0cb2d6dd1d68f12a533d04c822b68a80>
>
> Still queued??  Not very likely.
>
> It is a topic from almost 3 years ago, so it is either in an ancient
> released version, or it was discarded long time ago for some issues.
> Given that it is from Peff, it is very likely the former.
>
> https://git.kernel.org/pub/scm/git/git.git/commit/?h=15108de2fa0&id=15108de2fa0cd8f002a0551d14c84505a853071c
>
>
> That's v2.41.0-rc0~141 if my "git describe --contains" is counting
> correctly.
>
> My secretary will bill you for 30 minutes of my time and for making
> me miss a meeting with external folks with this ;-).

The email with the patch[1] seems to say Git 2.39.5 in the signature line.

    @@ -107,6 +111,7 @@ .SH NOTES
     .SH SEE ALSO
     .BR clone (2),
     .BR futex (2),
    +.BR futex_waitv (2),
     .BR get_robust_list (2),
     .BR set_robust_list (2),
     .BR set_tid_address (2),
    --
    2.39.5

That topic is not in any 2.39.* release.

† 1: https://lore.kernel.org/all/se6hm5gnd7cyjsby5q6pctkrws5ecp5gpnfjuy3zh2shd2abyj@tarta.nabijaczleweli.xyz/

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v2] futex_waitv.2: new page
  2026-02-10 15:54         ` Kristoffer Haugsbakk
@ 2026-02-10 18:39           ` Alejandro Colomar
  2026-02-11  7:35           ` Jeff King
  1 sibling, 0 replies; 53+ messages in thread
From: Alejandro Colomar @ 2026-02-10 18:39 UTC (permalink / raw)
  To: Kristoffer Haugsbakk; +Cc: git, наб, linux-man, Jeff King

[-- Attachment #1: Type: text/plain, Size: 3785 bytes --]

Hi Kristoffer, наб,

On 2026-02-10T16:54:02+0100, Kristoffer Haugsbakk wrote:
> On Tue, Feb 10, 2026, at 15:30, Alejandro Colomar wrote:
> > Hi!
> >
> > On 2026-02-10T15:17:55+0100, наб wrote:
> >> Hi!
> >>
> >> On Mon, Feb 09, 2026 at 11:35:53PM +0100, Alejandro Colomar wrote:
> >> > On 2026-02-07T23:00:49+0100, наб wrote:
> >> > > Signed-off-by: Ahelenia Ziemiańska <nabijaczleweli@nabijaczleweli.xyz>
> >> >
> >> > For some reason, the patch doesn't want to apply.  I don't see anything
> >> > obviously wrong, so it may be an issue in my side?
> >> >
> >> > 	Applying: futex_waitv.2: new page
> >> > 	error: affected file 'man2/futex_waitv.2' is beyond a symbolic link
> >> > 	error: man7/futex.7: does not exist in index
> >> > 	Patch failed at 0001 futex_waitv.2: new page
> >> > 	hint: Use 'git am --show-current-patch=diff' to see the failed patch
> >> > 	hint: When you have resolved this problem, run "git am --continue".
> >> > 	hint: If you prefer to skip this patch, run "git am --skip" instead.
> >> > 	hint: To restore the original branch and stop patching, run "git am --abort".
> >> > 	hint: Disable this message with "git config set advice.mergeConflict false"
> >> > 	Press any key to continue...
> >>
> >> Hm, I did recently set
> >>   $ git config diff.noprefix
> >>   true
> >> I didn't expect this to affect format-patch diffs
> >> (since it doesn't affect diffs shown by git add -p),
> >> or, if it did, I expected the designated consumer of format-patch
> >> diffs (am) to understand this. perhaps not;
> >> maybe -p0 to git am?
> >>
> >> A quick search yields
> >>   https://lore.kernel.org/git/xmqqy1o5op1i.fsf@gitster.g/t/#eaa0323ec4eed441b37caf96e1b136529b298dbac
> >> where you're in the thread and the maintainer says "queued" for a patch
> >> that would ignore noprefix for format-patch for this reason precisely.
> >> But clearly not, since my patches were with noprefix=true and came out -p0.
> >
> > Oh, that bites again!
> >
> > Junio, do you still have this queued?
> > <https://lore.kernel.org/git/xmqqy1o5op1i.fsf@gitster.g/t/#m6f42ff4f0cb2d6dd1d68f12a533d04c822b68a80>
> 
> See 15108de2 (Merge branch 'jk/format-patch-ignore-noprefix',
> 2023-03-21).

Ahhh, it seems that the OP is using a too-old version of git.  I saw at
the bottom of the patch 2.39.5.  But the fix was part of 2.41.0.

	$ git describe --contains ab89575387c02ea024163256826ad1c6dd2e4247
	v2.41.0-rc0~141^2

This seems reasonable.  наб, would you mind not using that flag unless
you use a recent-enough git(1) (I suspect you're on an old Debian)?  :)

	alx@devuan:~/src/linux/git/main$ git show v2.41.0 | grep ^Date
	Date:   2023-06-01 15:28:43 +0900
	Date:   2023-06-01 15:28:26 +0900
	alx@devuan:~/src/linux/git/main$ git show v2.39.5 | grep ^Date
	Date:   2024-05-30 17:22:58 -0700
	Date:   2024-05-30 16:52:52 -0700
	alx@devuan:~/src/linux/git/main$ git show v2.39.0 | grep ^Date
	Date:   2022-12-12 09:59:23 +0900
	Date:   2022-12-12 09:59:08 +0900


Have a lovely night!
Alex

> An aside but `format.noprefix` is not a boolean like `diff.noprefix`.
> It will be enabled with any value.
> 
> Is it standard to indicate this with the existing “If set,”, perhaps? Or
> should it say “enabled with any value”?
> 
> (+Cc Peff)
> 
>     format.noprefix::
>             If set, do not show any source or destination prefix in patches.
>             This is equivalent to the `diff.noprefix` option used by `git
>             diff` (but which is not respected by `format-patch`). Note that
>             by setting this, the receiver of any patches you generate will
>             have to apply them using the `-p0` option.

-- 
<https://www.alejandro-colomar.es>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v2] futex_waitv.2: new page
  2026-02-10 16:54         ` Junio C Hamano
  2026-02-10 17:11           ` Kristoffer Haugsbakk
@ 2026-02-10 18:44           ` Alejandro Colomar
  1 sibling, 0 replies; 53+ messages in thread
From: Alejandro Colomar @ 2026-02-10 18:44 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, наб, linux-man

[-- Attachment #1: Type: text/plain, Size: 1172 bytes --]

Hi Junio,

On 2026-02-10T08:54:13-0800, Junio C Hamano wrote:
> Alejandro Colomar <alx@kernel.org> writes:
> 
> > Junio, do you still have this queued?
> > <https://lore.kernel.org/git/xmqqy1o5op1i.fsf@gitster.g/t/#m6f42ff4f0cb2d6dd1d68f12a533d04c822b68a80>
> 
> Still queued??  Not very likely.
> 
> It is a topic from almost 3 years ago, so it is either in an ancient
> released version, or it was discarded long time ago for some issues.
> Given that it is from Peff, it is very likely the former.
> 
> https://git.kernel.org/pub/scm/git/git.git/commit/?h=15108de2fa0&id=15108de2fa0cd8f002a0551d14c84505a853071c    
> 
> That's v2.41.0-rc0~141 if my "git describe --contains" is counting
> correctly.
> 
> My secretary will bill you for 30 minutes of my time and for making
> me miss a meeting with external folks with this ;-).

Ohh, sorry!  :)

The problem seems to be still alive in some stable branch, probably in
oldstable distros.  I guess I'll have to deal every now and then with
those, depending on how many people discover diff.noprefix while still
using old versions.


Cheers,
Alex

-- 
<https://www.alejandro-colomar.es>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v2] futex_waitv.2: new page
  2026-02-07 22:00 ` [PATCH v2] " наб
  2026-02-09 22:35   ` Alejandro Colomar
@ 2026-02-10 20:05   ` Alejandro Colomar
  2026-02-10 20:32     ` [PATCH v3] " наб
  1 sibling, 1 reply; 53+ messages in thread
From: Alejandro Colomar @ 2026-02-10 20:05 UTC (permalink / raw)
  To: наб; +Cc: linux-man

[-- Attachment #1: Type: text/plain, Size: 12554 bytes --]

Hi,

On 2026-02-07T23:00:49+0100, наб wrote:
> Signed-off-by: Ahelenia Ziemiańska <nabijaczleweli@nabijaczleweli.xyz>
> ---
>  man/man2/futex_waitv.2 | 407 +++++++++++++++++++++++++++++++++++++++++
>  man/man7/futex.7       |   9 +-
>  2 files changed, 414 insertions(+), 2 deletions(-)
>  create mode 100644 man/man2/futex_waitv.2
> 
> diff --git man/man2/futex_waitv.2 man/man2/futex_waitv.2
> new file mode 100644
> index 000000000..2645f4147
> --- /dev/null
> +++ man/man2/futex_waitv.2
> @@ -0,0 +1,407 @@
> +.\" Copyright, the authors of the Linux man-pages project
> +.\"
> +.\" SPDX-License-Identifier: MIT
> +.\"
> +.TH futex_waitv 2 (date) "Linux man-pages (unreleased)"
> +.SH NAME
> +futex_waitv \- wait for FUTEX_WAKE operation on multiple futexes
> +.SH LIBRARY
> +Standard C library
> +.RI ( libc ,\~ \-lc )
> +.SH SYNOPSIS
> +.nf
> +.BR "#include <linux/futex.h>" "  /* Definition of " "struct futex_waitv" " */"
> +.BR "#include <sys/syscall.h>" "  /* Definition of " SYS_* " constants */"
> +.B #include <unistd.h>
> +.B #include <time.h>
> +.P
> +.BR "long syscall(" "unsigned int nr_futexes;"
> +.BI "             SYS_futex_waitv, struct futex_waitv " waiters [ nr_futexes ],
> +.BI "             unsigned int " nr_futexes ", unsigned int " flags ,
> +.BI "             const struct timespec *_Nullable " timeout ", clockid_t " clockid ");"
> +.fi
> +.P
> +.EX
> +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 pointed to by the addresses
> +.IR waiters []. uaddr
> +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 not NULL,
> +.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.
> +If
> +.I timeout
> +is NULL, the call blocks indefinitely.
> +.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 one of
> +.BR FUTEX2_SIZE_*
> +ORed with some of the flags below.
> +.P
> +C programs should assign to
> +.I uaddr
> +by casting a pointer to
> +.B uintptr_t
> +to ensure the top bits are cleared on 32-bit systems.
> +.TP
> +.BR FUTEX2_SIZE_U32
> +.I val
> +and
> +.I *uaddr
> +are 32-bit unsigned integers.
> +.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 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.
> +.\" commit cec199c5e39bde7191a08087cc3d002ccfab31ff
> +.\" Author: Peter Zijlstra <peterz@infradead.org>
> +.\" Date:   Wed Apr 16 18:29:16 2025 +0200
> +.\"
> +.\"     futex: Implement FUTEX2_NUMA
> +.\"
> +.\" FUTEX2_MPOL is not documented or used anywhere;
> +.\" it's unclear to me what it does
> +.\" (defined in commit c042c505210dc3453f378df432c10fff3d471bc5
> +.\"  "futex: Implement FUTEX2_MPOL")
> +.TP
> +.B FUTEX2_PRIVATE
> +By default, the futex is shared
> +.RB "(like " 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.
> +.\"
> +.\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
> +.\"
> +.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
> +was not NULL and did not point to a valid user-space address.
> +.TP
> +.B EFAULT
> +Any
> +.IR waiters []. uaddr
> +field is not a valid user-space address.
> +.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
> +.BR FUTEX2_SIZE_* .
> +.TP
> +.B EINVAL
> +.I flags
> +was not 0.
> +.TP
> +.B EINVAL
> +.I nr_futexes
> +was not in
> +[1,
> +.B FUTEX_WAITV_MAX
> +(128)].
> +.TP
> +.B EINVAL
> +.I timeout
> +was not NULL and
> +.I clockid
> +was not a valid clock
> +.RB ( CLOCK_MONOTONIC
> +or
> +.BR CLOCK_REALTIME ).
> +.TP
> +.B EINVAL
> +.I *timeout
> +is denormal (before epoch or
> +.I tv_nsec
> +more than 999'999'999).
> +.TP
> +.B EINVAL
> +Any
> +.IR waiters []. flags
> +field contains an unknown flag.
> +.TP
> +.B EINVAL
> +Any
> +.IR waiters []. flags
> +field is missing a
> +.B FUTEX2_SIZE_*
> +flag or has a size flag different than
> +.BR FUTEX2_SIZE_U32
> +set.
> +.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 index of the biggest NUMA domain
> +(for example,
> +.B FUTEX2_SIZE_U8
> +and there are more than 255 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
> +.I timeout
> +was not NULL and no futex was woken before the timeout elapsed.
> +.TP
> +.BR EAGAIN " or " EWOULDBLOCK
> +The value pointed to by
> +.I uaddr
> +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 NOTES
> +.BR FUTEX2_SIZE_U8 ,
> +.BR FUTEX2_SIZE_U16 ,
> +and
> +.B FUTEX2_SIZE_U64
> +where
> +.I val
> +and
> +.I *uaddr
> +are 8, 16, or 64 bits are defined, but not implemented
> +.RB ( EINVAL ).
> +.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 ().
> +.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!
> +Connection timed out
> +.EE
> +.P
> +.EX
> +#include <errno.h>
> +#include <inttypes.h>
> +#include <linux/futex.h>
> +#include <pthread.h>
> +#include <stdatomic.h>
> +#include <stdint.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <sys/syscall.h>
> +#include <time.h>
> +#include <unistd.h>
> +\&
> +void *
> +worker(void *arg)
> +{
> +	_Atomic uint32_t *futex = arg;
> +\&
> +	usleep(*futex * 10000);
> +	*futex *= 2;
> +	syscall(SYS_futex, futex, FUTEX_WAKE_PRIVATE, 1);
> +	return NULL;
> +}
> +\&
> +int
> +main(void)
> +{
> +#define WORKERS 10
> +	_Atomic uint32_t futexes[WORKERS];
> +\&
> +	uint8_t init[WORKERS];
> +	getentropy(init, sizeof(init));
> +	init[0] = init[1] = init[2];
> +	for (int i = 0; i < WORKERS; ++i) {
> +		printf("%" PRIu8 "\\t", init[i]);
> +		atomic_init(&futexes[i], init[i]);
> +		pthread_create(&(pthread_t){}, NULL, worker, &futexes[i]);
> +	}
> +	putchar('\\n');

The escape character should be printed as \[rs].  rs is reverse solidus.

> +\&
> +	struct futex_waitv waiters[WORKERS] = {};
> +	for (int i = 0; i < WORKERS; ++i) {
> +		waiters[i].val   = futexes[i];
> +		waiters[i].uaddr = (uintptr_t)&futexes[i];
> +		waiters[i].flags = FUTEX2_SIZE_U32 | FUTEX2_PRIVATE;
> +	}
> +	for (;;) {
> +		struct timespec timeout

Expected ';' before clock_gettime.

Please make sure the example works fine.


Cheers,
Alex

> +		clock_gettime(CLOCK_MONOTONIC, &timeout);
> +		timeout.tv_sec += 1;
> +\&
> +		int woke = syscall(SYS_futex_waitv, waiters, WORKERS, 0, &timeout, CLOCK_MONOTONIC);
> +		if (woke == -1)
> +			break;
> +\&
> +		for (int i = 0; i < WORKERS; ++i) {
> +			if (futexes[i] != waiters[i].val)
> +				printf("%" PRIu32 "%.*s", futexes[i], i == woke, "!");
> +			putchar('\\t');
> +		}
> +		putchar('\\n');
> +\&
> +		for (int i = 0; i < WORKERS; ++i)
> +			waiters[i].val = futexes[i];
> +	}
> +	printf("%s\\n", strerror(errno));
> +}
> +.EE
> +.SH SEE ALSO
> +.ad l
> +.BR futex (2),
> +.BR FUTEX_WAIT (2const),
> +.BR FUTEX_WAKE (2const),
> +.BR futex (7)
> +.P
> +The following kernel source files:
> +.IP \[bu]
> +.I Documentation/userspace-api/futex2.rst
> +.IP \[bu]
> +.I kernel/futex/syscall.c
> +.IP \[bu]
> +.I kernel/futex/waitwake.c
> +.IP \[bu]
> +.I kernel/futex/futex.h
> diff --git man/man7/futex.7 man/man7/futex.7
> index 51c5d5d9b..d271144ff 100644
> --- man/man7/futex.7
> +++ man/man7/futex.7
> @@ -45,7 +45,9 @@ .SS Semantics
>  Any futex operation starts in user space,
>  but it may be necessary to communicate with the kernel using the
>  .BR futex (2)
> -system call.
> +or
> +.BR futex_waitv (2)
> +system calls.
>  .P
>  To "up" a futex, execute the proper assembler instructions that
>  will cause the host CPU to atomically increment the integer.
> @@ -72,7 +74,9 @@ .SS Semantics
>  .P
>  The
>  .BR futex (2)
> -system call can optionally be passed a timeout specifying how long
> +and
> +.BR futex_waitv (2)
> +system calls can optionally be passed a timeout specifying how long
>  the kernel should
>  wait for the futex to be upped.
>  In this case, semantics are more complex and the programmer is referred
> @@ -107,6 +111,7 @@ .SH NOTES
>  .SH SEE ALSO
>  .BR clone (2),
>  .BR futex (2),
> +.BR futex_waitv (2),
>  .BR get_robust_list (2),
>  .BR set_robust_list (2),
>  .BR set_tid_address (2),
> -- 
> 2.39.5



-- 
<https://www.alejandro-colomar.es>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [PATCH v3] futex_waitv.2: new page
  2026-02-10 20:05   ` Alejandro Colomar
@ 2026-02-10 20:32     ` наб
  2026-02-10 21:11       ` Alejandro Colomar
  0 siblings, 1 reply; 53+ messages in thread
From: наб @ 2026-02-10 20:32 UTC (permalink / raw)
  To: Alejandro Colomar; +Cc: linux-man

[-- Attachment #1: Type: text/plain, Size: 11438 bytes --]

Signed-off-by: Ahelenia Ziemiańska <nabijaczleweli@nabijaczleweli.xyz>
---
 man/man2/futex_waitv.2 | 407 +++++++++++++++++++++++++++++++++++++++++
 man/man7/futex.7       |   9 +-
 2 files changed, 414 insertions(+), 2 deletions(-)
 create mode 100644 man/man2/futex_waitv.2

diff --git u/man/man2/futex_waitv.2 p/man/man2/futex_waitv.2
new file mode 100644
index 000000000..2ec6e4b50
--- /dev/null
+++ p/man/man2/futex_waitv.2
@@ -0,0 +1,407 @@
+.\" Copyright, the authors of the Linux man-pages project
+.\"
+.\" SPDX-License-Identifier: MIT
+.\"
+.TH futex_waitv 2 (date) "Linux man-pages (unreleased)"
+.SH NAME
+futex_waitv \- wait for FUTEX_WAKE operation on multiple futexes
+.SH LIBRARY
+Standard C library
+.RI ( libc ,\~ \-lc )
+.SH SYNOPSIS
+.nf
+.BR "#include <linux/futex.h>" "  /* Definition of " "struct futex_waitv" " */"
+.BR "#include <sys/syscall.h>" "  /* Definition of " SYS_* " constants */"
+.B #include <unistd.h>
+.B #include <time.h>
+.P
+.BR "long syscall(" "unsigned int nr_futexes;"
+.BI "             SYS_futex_waitv, struct futex_waitv " waiters [ nr_futexes ],
+.BI "             unsigned int " nr_futexes ", unsigned int " flags ,
+.BI "             const struct timespec *_Nullable " timeout ", clockid_t " clockid ");"
+.fi
+.P
+.EX
+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 pointed to by the addresses
+.IR waiters []. uaddr
+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 not NULL,
+.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.
+If
+.I timeout
+is NULL, the call blocks indefinitely.
+.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 one of
+.BR FUTEX2_SIZE_*
+ORed with some of the flags below.
+.P
+C programs should assign to
+.I uaddr
+by casting a pointer to
+.B uintptr_t
+to ensure the top bits are cleared on 32-bit systems.
+.TP
+.BR FUTEX2_SIZE_U32
+.I val
+and
+.I *uaddr
+are 32-bit unsigned integers.
+.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 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.
+.\" commit cec199c5e39bde7191a08087cc3d002ccfab31ff
+.\" Author: Peter Zijlstra <peterz@infradead.org>
+.\" Date:   Wed Apr 16 18:29:16 2025 +0200
+.\"
+.\"     futex: Implement FUTEX2_NUMA
+.\"
+.\" FUTEX2_MPOL is not documented or used anywhere;
+.\" it's unclear to me what it does
+.\" (defined in commit c042c505210dc3453f378df432c10fff3d471bc5
+.\"  "futex: Implement FUTEX2_MPOL")
+.TP
+.B FUTEX2_PRIVATE
+By default, the futex is shared
+.RB "(like " 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.
+.\"
+.\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.\"
+.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
+was not NULL and did not point to a valid user-space address.
+.TP
+.B EFAULT
+Any
+.IR waiters []. uaddr
+field is not a valid user-space address.
+.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
+.BR FUTEX2_SIZE_* .
+.TP
+.B EINVAL
+.I flags
+was not 0.
+.TP
+.B EINVAL
+.I nr_futexes
+was not in
+[1,
+.B FUTEX_WAITV_MAX
+(128)].
+.TP
+.B EINVAL
+.I timeout
+was not NULL and
+.I clockid
+was not a valid clock
+.RB ( CLOCK_MONOTONIC
+or
+.BR CLOCK_REALTIME ).
+.TP
+.B EINVAL
+.I *timeout
+is denormal (before epoch or
+.I tv_nsec
+more than 999'999'999).
+.TP
+.B EINVAL
+Any
+.IR waiters []. flags
+field contains an unknown flag.
+.TP
+.B EINVAL
+Any
+.IR waiters []. flags
+field is missing a
+.B FUTEX2_SIZE_*
+flag or has a size flag different than
+.BR FUTEX2_SIZE_U32
+set.
+.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 index of the biggest NUMA domain
+(for example,
+.B FUTEX2_SIZE_U8
+and there are more than 255 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
+.I timeout
+was not NULL and no futex was woken before the timeout elapsed.
+.TP
+.BR EAGAIN " or " EWOULDBLOCK
+The value pointed to by
+.I uaddr
+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 NOTES
+.BR FUTEX2_SIZE_U8 ,
+.BR FUTEX2_SIZE_U16 ,
+and
+.B FUTEX2_SIZE_U64
+where
+.I val
+and
+.I *uaddr
+are 8, 16, or 64 bits are defined, but not implemented
+.RB ( EINVAL ).
+.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 ().
+.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!
+Connection timed out
+.EE
+.P
+.EX
+#include <errno.h>
+#include <inttypes.h>
+#include <linux/futex.h>
+#include <pthread.h>
+#include <stdatomic.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/syscall.h>
+#include <time.h>
+#include <unistd.h>
+\&
+void *
+worker(void *arg)
+{
+	_Atomic uint32_t *futex = arg;
+\&
+	usleep(*futex * 10000);
+	*futex *= 2;
+	syscall(SYS_futex, futex, FUTEX_WAKE_PRIVATE, 1);
+	return NULL;
+}
+\&
+int
+main(void)
+{
+#define WORKERS 10
+	_Atomic uint32_t futexes[WORKERS];
+\&
+	uint8_t init[WORKERS];
+	getentropy(init, sizeof(init));
+	init[0] = init[1] = init[2];
+	for (int i = 0; i < WORKERS; ++i) {
+		printf("%" PRIu8 "\[rs]t", init[i]);
+		atomic_init(&futexes[i], init[i]);
+		pthread_create(&(pthread_t){}, NULL, worker, &futexes[i]);
+	}
+	putchar('\[rs]n');
+\&
+	struct futex_waitv waiters[WORKERS] = {};
+	for (int i = 0; i < WORKERS; ++i) {
+		waiters[i].val   = futexes[i];
+		waiters[i].uaddr = (uintptr_t)&futexes[i];
+		waiters[i].flags = FUTEX2_SIZE_U32 | FUTEX2_PRIVATE;
+	}
+	for (;;) {
+		struct timespec timeout;
+		clock_gettime(CLOCK_MONOTONIC, &timeout);
+		timeout.tv_sec += 1;
+\&
+		int woke = syscall(SYS_futex_waitv, waiters, WORKERS, 0, &timeout, CLOCK_MONOTONIC);
+		if (woke == -1 && (errno != EAGAIN && errno != EWOULDBLOCK))
+			break;
+\&
+		for (int i = 0; i < WORKERS; ++i) {
+			if (futexes[i] != waiters[i].val)
+				printf("%" PRIu32 "%.*s", futexes[i], i == woke, "!");
+			putchar('\[rs]t');
+		}
+		putchar('\[rs]n');
+\&
+		for (int i = 0; i < WORKERS; ++i)
+			waiters[i].val = futexes[i];
+	}
+	printf("%s\[rs]n", strerror(errno));
+}
+.EE
+.SH SEE ALSO
+.ad l
+.BR futex (2),
+.BR FUTEX_WAIT (2const),
+.BR FUTEX_WAKE (2const),
+.BR futex (7)
+.P
+The following kernel source files:
+.IP \[bu]
+.I Documentation/userspace-api/futex2.rst
+.IP \[bu]
+.I kernel/futex/syscall.c
+.IP \[bu]
+.I kernel/futex/waitwake.c
+.IP \[bu]
+.I kernel/futex/futex.h
diff --git u/man/man7/futex.7 p/man/man7/futex.7
index 51c5d5d9b..d271144ff 100644
--- u/man/man7/futex.7
+++ p/man/man7/futex.7
@@ -45,7 +45,9 @@ .SS Semantics
 Any futex operation starts in user space,
 but it may be necessary to communicate with the kernel using the
 .BR futex (2)
-system call.
+or
+.BR futex_waitv (2)
+system calls.
 .P
 To "up" a futex, execute the proper assembler instructions that
 will cause the host CPU to atomically increment the integer.
@@ -72,7 +74,9 @@ .SS Semantics
 .P
 The
 .BR futex (2)
-system call can optionally be passed a timeout specifying how long
+and
+.BR futex_waitv (2)
+system calls can optionally be passed a timeout specifying how long
 the kernel should
 wait for the futex to be upped.
 In this case, semantics are more complex and the programmer is referred
@@ -107,6 +111,7 @@ .SH NOTES
 .SH SEE ALSO
 .BR clone (2),
 .BR futex (2),
+.BR futex_waitv (2),
 .BR get_robust_list (2),
 .BR set_robust_list (2),
 .BR set_tid_address (2),
-- 
2.39.5

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* Re: [PATCH v3] futex_waitv.2: new page
  2026-02-10 20:32     ` [PATCH v3] " наб
@ 2026-02-10 21:11       ` Alejandro Colomar
  2026-02-11  4:00         ` [PATCH v4] " наб
  0 siblings, 1 reply; 53+ messages in thread
From: Alejandro Colomar @ 2026-02-10 21:11 UTC (permalink / raw)
  To: наб; +Cc: linux-man

[-- Attachment #1: Type: text/plain, Size: 6058 bytes --]

Hi,

On 2026-02-10T21:32:18+0100, наб wrote:
> Signed-off-by: Ahelenia Ziemiańska <nabijaczleweli@nabijaczleweli.xyz>
> ---
>  man/man2/futex_waitv.2 | 407 +++++++++++++++++++++++++++++++++++++++++
>  man/man7/futex.7       |   9 +-
>  2 files changed, 414 insertions(+), 2 deletions(-)
>  create mode 100644 man/man2/futex_waitv.2
> 
> diff --git u/man/man2/futex_waitv.2 p/man/man2/futex_waitv.2
> new file mode 100644
> index 000000000..2ec6e4b50
> --- /dev/null
> +++ p/man/man2/futex_waitv.2
> @@ -0,0 +1,407 @@
[...]

> +.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 ().
> +.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!
> +Connection timed out
> +.EE
> +.P
> +.EX

Please wrap the program in comments so that it's checked by the build
system.  See for example:

	eventfd.2:381:.\" SRC BEGIN (eventfd.c)
	eventfd.2:436:.\" SRC END

> +#include <errno.h>
> +#include <inttypes.h>
> +#include <linux/futex.h>
> +#include <pthread.h>
> +#include <stdatomic.h>
> +#include <stdint.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <sys/syscall.h>
> +#include <time.h>
> +#include <unistd.h>
> +\&
> +void *
> +worker(void *arg)
> +{
> +	_Atomic uint32_t *futex = arg;

Please use two spaces to separate the type from the identifier:

	_Atomic uint32_t  *futex;

> +\&
> +	usleep(*futex * 10000);
> +	*futex *= 2;
> +	syscall(SYS_futex, futex, FUTEX_WAKE_PRIVATE, 1);

Is this allowed?  FUTEX_WAIT(2const) documents a 5th parameter.

     long syscall(SYS_futex, uint32_t *uaddr, FUTEX_WAIT, uint32_t val,
                  const struct timespec *_Nullable timeout);

You may get away because uninitialized values get a NULL often, but
is this reliable?  And even if it were reliable on glibc, would it be
good to use it in a manual page?

Also, please define a wrapper function futex_wait_private() that has the
following prototype:

	static inline long
	my_futex_wait_private(uint32_t *uaddr, uint32_t val,
	                      const struct timespec *_Nullable timeout);

> +	return NULL;
> +}
> +\&
> +int
> +main(void)
> +{
> +#define WORKERS 10
> +	_Atomic uint32_t futexes[WORKERS];
> +\&
> +	uint8_t init[WORKERS];

Please separate declarations from code with a blank line (kernel style).

> +	getentropy(init, sizeof(init));
> +	init[0] = init[1] = init[2];
> +	for (int i = 0; i < WORKERS; ++i) {

I'd prefer using countof() in things like this.

> +		printf("%" PRIu8 "\[rs]t", init[i]);
> +		atomic_init(&futexes[i], init[i]);
> +		pthread_create(&(pthread_t){}, NULL, worker, &futexes[i]);

I've now pushed a patch documenting _Nullable in pthread_create(3).

> +	}
> +	putchar('\[rs]n');
> +\&
> +	struct futex_waitv waiters[WORKERS] = {};
> +	for (int i = 0; i < WORKERS; ++i) {
> +		waiters[i].val   = futexes[i];
> +		waiters[i].uaddr = (uintptr_t)&futexes[i];
> +		waiters[i].flags = FUTEX2_SIZE_U32 | FUTEX2_PRIVATE;
> +	}
> +	for (;;) {
> +		struct timespec timeout;
> +		clock_gettime(CLOCK_MONOTONIC, &timeout);
> +		timeout.tv_sec += 1;
> +\&
> +		int woke = syscall(SYS_futex_waitv, waiters, WORKERS, 0, &timeout, CLOCK_MONOTONIC);

Please write a my_futex_waitv() static inline wrapper.

Please separate declarations from code, and avoid initializations except
when really needed.

> +		if (woke == -1 && (errno != EAGAIN && errno != EWOULDBLOCK))
> +			break;
> +\&
> +		for (int i = 0; i < WORKERS; ++i) {
> +			if (futexes[i] != waiters[i].val)
> +				printf("%" PRIu32 "%.*s", futexes[i], i == woke, "!");

I'd prefer to avoid "%.*s" unless really needed.  That's something to be
used exclusively with [[gnu::nonstring]].  If I'm reading this
correctly, you really want "%s" with i==woke ? "!" : "".

> +			putchar('\[rs]t');
> +		}
> +		putchar('\[rs]n');
> +\&
> +		for (int i = 0; i < WORKERS; ++i)
> +			waiters[i].val = futexes[i];
> +	}
> +	printf("%s\[rs]n", strerror(errno));

I expect strerror(3) should go to stderr.  Do we really want stdout?


Cheers,
Alex

> +}
> +.EE
> +.SH SEE ALSO
> +.ad l
> +.BR futex (2),
> +.BR FUTEX_WAIT (2const),
> +.BR FUTEX_WAKE (2const),
> +.BR futex (7)
> +.P
> +The following kernel source files:
> +.IP \[bu]
> +.I Documentation/userspace-api/futex2.rst
> +.IP \[bu]
> +.I kernel/futex/syscall.c
> +.IP \[bu]
> +.I kernel/futex/waitwake.c
> +.IP \[bu]
> +.I kernel/futex/futex.h
> diff --git u/man/man7/futex.7 p/man/man7/futex.7
> index 51c5d5d9b..d271144ff 100644
> --- u/man/man7/futex.7
> +++ p/man/man7/futex.7
> @@ -45,7 +45,9 @@ .SS Semantics
>  Any futex operation starts in user space,
>  but it may be necessary to communicate with the kernel using the
>  .BR futex (2)
> -system call.
> +or
> +.BR futex_waitv (2)
> +system calls.
>  .P
>  To "up" a futex, execute the proper assembler instructions that
>  will cause the host CPU to atomically increment the integer.
> @@ -72,7 +74,9 @@ .SS Semantics
>  .P
>  The
>  .BR futex (2)
> -system call can optionally be passed a timeout specifying how long
> +and
> +.BR futex_waitv (2)
> +system calls can optionally be passed a timeout specifying how long
>  the kernel should
>  wait for the futex to be upped.
>  In this case, semantics are more complex and the programmer is referred
> @@ -107,6 +111,7 @@ .SH NOTES
>  .SH SEE ALSO
>  .BR clone (2),
>  .BR futex (2),
> +.BR futex_waitv (2),
>  .BR get_robust_list (2),
>  .BR set_robust_list (2),
>  .BR set_tid_address (2),
> -- 
> 2.39.5



-- 
<https://www.alejandro-colomar.es>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [PATCH v4] futex_waitv.2: new page
  2026-02-10 21:11       ` Alejandro Colomar
@ 2026-02-11  4:00         ` наб
  2026-02-11 13:23           ` Alejandro Colomar
  0 siblings, 1 reply; 53+ messages in thread
From: наб @ 2026-02-11  4:00 UTC (permalink / raw)
  To: Alejandro Colomar; +Cc: linux-man

[-- Attachment #1: Type: text/plain, Size: 12073 bytes --]

Signed-off-by: Ahelenia Ziemiańska <nabijaczleweli@nabijaczleweli.xyz>
---
 man/man2/futex_waitv.2 | 426 +++++++++++++++++++++++++++++++++++++++++
 man/man7/futex.7       |   9 +-
 2 files changed, 433 insertions(+), 2 deletions(-)
 create mode 100644 man/man2/futex_waitv.2

diff --git u/man/man2/futex_waitv.2 p/man/man2/futex_waitv.2
new file mode 100644
index 000000000..b05eb08ef
--- /dev/null
+++ p/man/man2/futex_waitv.2
@@ -0,0 +1,426 @@
+.\" Copyright, the authors of the Linux man-pages project
+.\"
+.\" SPDX-License-Identifier: MIT
+.\"
+.TH futex_waitv 2 (date) "Linux man-pages (unreleased)"
+.SH NAME
+futex_waitv \- wait for FUTEX_WAKE operation on multiple futexes
+.SH LIBRARY
+Standard C library
+.RI ( libc ,\~ \-lc )
+.SH SYNOPSIS
+.nf
+.BR "#include <linux/futex.h>" "  /* Definition of " "struct futex_waitv" " */"
+.BR "#include <sys/syscall.h>" "  /* Definition of " SYS_* " constants */"
+.B #include <unistd.h>
+.B #include <time.h>
+.P
+.BR "long syscall(" "unsigned int nr_futexes;"
+.BI "             SYS_futex_waitv, struct futex_waitv " waiters [ nr_futexes ],
+.BI "             unsigned int " nr_futexes ", unsigned int " flags ,
+.BI "             const struct timespec *_Nullable " timeout ", clockid_t " clockid ");"
+.fi
+.P
+.EX
+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 pointed to by the addresses
+.IR waiters []. uaddr
+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 not NULL,
+.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.
+If
+.I timeout
+is NULL, the call blocks indefinitely.
+.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 one of
+.BR FUTEX2_SIZE_*
+ORed with some of the flags below.
+.P
+C programs should assign to
+.I uaddr
+by casting a pointer to
+.B uintptr_t
+to ensure the top bits are cleared on 32-bit systems.
+.TP
+.BR FUTEX2_SIZE_U32
+.I val
+and
+.I *uaddr
+are 32-bit unsigned integers.
+.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 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.
+.\" commit cec199c5e39bde7191a08087cc3d002ccfab31ff
+.\" Author: Peter Zijlstra <peterz@infradead.org>
+.\" Date:   Wed Apr 16 18:29:16 2025 +0200
+.\"
+.\"     futex: Implement FUTEX2_NUMA
+.\"
+.\" FUTEX2_MPOL is not documented or used anywhere;
+.\" it's unclear to me what it does
+.\" (defined in commit c042c505210dc3453f378df432c10fff3d471bc5
+.\"  "futex: Implement FUTEX2_MPOL")
+.TP
+.B FUTEX2_PRIVATE
+By default, the futex is shared
+.RB "(like " 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.
+.\"
+.\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.\"
+.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
+was not NULL and did not point to a valid user-space address.
+.TP
+.B EFAULT
+Any
+.IR waiters []. uaddr
+field is not a valid user-space address.
+.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
+.BR FUTEX2_SIZE_* .
+.TP
+.B EINVAL
+.I flags
+was not 0.
+.TP
+.B EINVAL
+.I nr_futexes
+was not in
+[1,
+.B FUTEX_WAITV_MAX
+(128)].
+.TP
+.B EINVAL
+.I timeout
+was not NULL and
+.I clockid
+was not a valid clock
+.RB ( CLOCK_MONOTONIC
+or
+.BR CLOCK_REALTIME ).
+.TP
+.B EINVAL
+.I *timeout
+is denormal (before epoch or
+.I tv_nsec
+more than 999'999'999).
+.TP
+.B EINVAL
+Any
+.IR waiters []. flags
+field contains an unknown flag.
+.TP
+.B EINVAL
+Any
+.IR waiters []. flags
+field is missing a
+.B FUTEX2_SIZE_*
+flag or has a size flag different than
+.BR FUTEX2_SIZE_U32
+set.
+.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 index of the biggest NUMA domain
+(for example,
+.B FUTEX2_SIZE_U8
+and there are more than 255 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
+.I timeout
+was not NULL and no futex was woken before the timeout elapsed.
+.TP
+.BR EAGAIN " or " EWOULDBLOCK
+The value pointed to by
+.I uaddr
+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 NOTES
+.BR FUTEX2_SIZE_U8 ,
+.BR FUTEX2_SIZE_U16 ,
+and
+.B FUTEX2_SIZE_U64
+where
+.I val
+and
+.I *uaddr
+are 8, 16, or 64 bits are defined, but not implemented
+.RB ( EINVAL ).
+.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 ().
+.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!
+Connection timed out
+.EE
+.P
+.\" SRC BEGIN (futex_waitv.c)
+.EX
+#include <errno.h>
+#include <inttypes.h>
+#include <linux/futex.h>
+#include <pthread.h>
+#include <stdatomic.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_wait_private(_Atomic uint32_t  *uaddr, uint32_t  val,
+                      const struct timespec *_Nullable  timeout)
+{
+	return syscall(SYS_futex, uaddr, FUTEX_WAKE_PRIVATE, val, timeout);
+}
+\&
+static inline long
+my_futex_waitv(struct futex_waitv  *waiters, unsigned int  nr_futexes,
+               unsigned int  flags, const struct timespec *_Nullable  timeout,
+               clockid_t  clockid)
+{
+	return syscall(SYS_futex_waitv, waiters, nr_futexes, flags, timeout, clockid);
+}
+\&
+void *
+worker(void  *arg)
+{
+	_Atomic uint32_t  *futex = arg;
+\&
+	usleep(*futex * 10000);
+	*futex *= 2;
+	my_futex_wait_private(futex, 1, NULL);
+	return NULL;
+}
+\&
+int
+main(void)
+{
+	_Atomic uint32_t  futexes[10];
+	uint8_t  init[countof(futexes)];
+	struct futex_waitv waiters[countof(futexes)] = {};
+	int  i;
+\&
+	getentropy(init, sizeof(init));
+	init[0] = init[1] = init[2];
+	for (i = 0; i < countof(futexes); ++i) {
+		printf("%" PRIu8 "\[rs]t", init[i]);
+		atomic_init(&futexes[i], init[i]);
+		pthread_create(&(pthread_t){}, NULL, worker, &futexes[i]);
+	}
+	putchar('\[rs]n');
+\&
+	for (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 (;;) {
+		struct timespec  timeout;
+		int  woke;
+\&
+		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))
+			break;
+\&
+		for (i = 0; i < countof(futexes); ++i) {
+			if (futexes[i] != waiters[i].val)
+				printf("%" PRIu32 "%s", futexes[i], i == woke ? "!" : "");
+			putchar('\[rs]t');
+		}
+		putchar('\[rs]n');
+\&
+		for (i = 0; i < countof(futexes); ++i)
+			waiters[i].val = futexes[i];
+	}
+	fprintf(stderr, "%s\[rs]n", strerror(errno));
+}
+.EE
+.\" SRC END
+.SH SEE ALSO
+.ad l
+.BR futex (2),
+.BR FUTEX_WAIT (2const),
+.BR FUTEX_WAKE (2const),
+.BR futex (7)
+.P
+The following kernel source files:
+.IP \[bu]
+.I Documentation/userspace-api/futex2.rst
+.IP \[bu]
+.I kernel/futex/syscall.c
+.IP \[bu]
+.I kernel/futex/waitwake.c
+.IP \[bu]
+.I kernel/futex/futex.h
diff --git u/man/man7/futex.7 p/man/man7/futex.7
index 51c5d5d9b..d271144ff 100644
--- u/man/man7/futex.7
+++ p/man/man7/futex.7
@@ -45,7 +45,9 @@ .SS Semantics
 Any futex operation starts in user space,
 but it may be necessary to communicate with the kernel using the
 .BR futex (2)
-system call.
+or
+.BR futex_waitv (2)
+system calls.
 .P
 To "up" a futex, execute the proper assembler instructions that
 will cause the host CPU to atomically increment the integer.
@@ -72,7 +74,9 @@ .SS Semantics
 .P
 The
 .BR futex (2)
-system call can optionally be passed a timeout specifying how long
+and
+.BR futex_waitv (2)
+system calls can optionally be passed a timeout specifying how long
 the kernel should
 wait for the futex to be upped.
 In this case, semantics are more complex and the programmer is referred
@@ -107,6 +111,7 @@ .SH NOTES
 .SH SEE ALSO
 .BR clone (2),
 .BR futex (2),
+.BR futex_waitv (2),
 .BR get_robust_list (2),
 .BR set_robust_list (2),
 .BR set_tid_address (2),
-- 
2.39.5

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* Re: [PATCH v2] futex_waitv.2: new page
  2026-02-10 15:54         ` Kristoffer Haugsbakk
  2026-02-10 18:39           ` Alejandro Colomar
@ 2026-02-11  7:35           ` Jeff King
  2026-02-11  8:15             ` Kristoffer Haugsbakk
  2026-02-11 15:43             ` Junio C Hamano
  1 sibling, 2 replies; 53+ messages in thread
From: Jeff King @ 2026-02-11  7:35 UTC (permalink / raw)
  To: Kristoffer Haugsbakk
  Cc: Alejandro Colomar, git, наб, linux-man

On Tue, Feb 10, 2026 at 04:54:02PM +0100, Kristoffer Haugsbakk wrote:

> An aside but `format.noprefix` is not a boolean like `diff.noprefix`.
> It will be enabled with any value.

Huh, that's weird. Indeed, the code is:

          if (!strcmp(var, "format.noprefix")) {
                  format_no_prefix = 1;
                  return 0;
          }

which seems just wrong. I cannot think of any other case where the
_existence_ of a config variable determines the outcome, rather than the
assigned value. So I would be inclined to call this a bug and switch it
to use git_config_bool().

Technically that is not backwards-compatible, but I really think the
existing behavior is just a bug. It was not something intended and is
contrary to how the rest of Git works. Presumably nobody noticed because
why in the world would you set it to false in the first place?

> Is it standard to indicate this with the existing “If set,”, perhaps? Or
> should it say “enabled with any value”?
> 
> (+Cc Peff)
> 
>     format.noprefix::
>             If set, do not show any source or destination prefix in patches.
>             This is equivalent to the `diff.noprefix` option used by `git
>             diff` (but which is not respected by `format-patch`). Note that
>             by setting this, the receiver of any patches you generate will
>             have to apply them using the `-p0` option.

We usually use "if set" in the config documentation to refer to options
being set to the true value. So I think the documentation text is OK and
would match the code, once fixed.

Kristoffer, do you want to produce a patch to fix the code? I feel like
finding it was 99% of the work. ;)

-Peff

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v2] futex_waitv.2: new page
  2026-02-11  7:35           ` Jeff King
@ 2026-02-11  8:15             ` Kristoffer Haugsbakk
  2026-02-11 15:43             ` Junio C Hamano
  1 sibling, 0 replies; 53+ messages in thread
From: Kristoffer Haugsbakk @ 2026-02-11  8:15 UTC (permalink / raw)
  To: Jeff King; +Cc: Alejandro Colomar, git, наб, linux-man

On Wed, Feb 11, 2026, at 08:35, Jeff King wrote:
>>[snip]
>
> Kristoffer, do you want to produce a patch to fix the code? I feel like
> finding it was 99% of the work. ;)

Sure, I want to take a look at it. :)

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v4] futex_waitv.2: new page
  2026-02-11  4:00         ` [PATCH v4] " наб
@ 2026-02-11 13:23           ` Alejandro Colomar
  2026-02-11 13:51             ` [PATCH v5] " наб
  0 siblings, 1 reply; 53+ messages in thread
From: Alejandro Colomar @ 2026-02-11 13:23 UTC (permalink / raw)
  To: наб; +Cc: linux-man

[-- Attachment #1: Type: text/plain, Size: 14529 bytes --]

Hi наб,

On 2026-02-11T05:00:26+0100, наб wrote:
> Signed-off-by: Ahelenia Ziemiańska <nabijaczleweli@nabijaczleweli.xyz>
> ---
>  man/man2/futex_waitv.2 | 426 +++++++++++++++++++++++++++++++++++++++++
>  man/man7/futex.7       |   9 +-
>  2 files changed, 433 insertions(+), 2 deletions(-)
>  create mode 100644 man/man2/futex_waitv.2
> 
> diff --git u/man/man2/futex_waitv.2 p/man/man2/futex_waitv.2
> new file mode 100644
> index 000000000..b05eb08ef
> --- /dev/null
> +++ p/man/man2/futex_waitv.2
> @@ -0,0 +1,426 @@
> +.\" Copyright, the authors of the Linux man-pages project
> +.\"
> +.\" SPDX-License-Identifier: MIT
> +.\"
> +.TH futex_waitv 2 (date) "Linux man-pages (unreleased)"
> +.SH NAME
> +futex_waitv \- wait for FUTEX_WAKE operation on multiple futexes
> +.SH LIBRARY
> +Standard C library
> +.RI ( libc ,\~ \-lc )
> +.SH SYNOPSIS
> +.nf
> +.BR "#include <linux/futex.h>" "  /* Definition of " "struct futex_waitv" " */"

The comment should mention the FUTEX_ constants, and not the structure
(which should be documented next to the structure).

> +.BR "#include <sys/syscall.h>" "  /* Definition of " SYS_* " constants */"
> +.B #include <unistd.h>
> +.B #include <time.h>
> +.P
> +.BR "long syscall(" "unsigned int nr_futexes;"

Let's call this just 'n'.  I have been wanting to globally trim
parameter names that are unnecessarily verbose.  Let's not add more.

> +.BI "             SYS_futex_waitv, struct futex_waitv " waiters [ nr_futexes ],
> +.BI "             unsigned int " nr_futexes ", unsigned int " flags ,
> +.BI "             const struct timespec *_Nullable " timeout ", clockid_t " clockid ");"
> +.fi
> +.P
> +.EX

Please include a header for this structure (consider as if the other
headers were not included).

> +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 pointed to by the addresses
> +.IR waiters []. uaddr
> +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 not NULL,
> +.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.
> +If
> +.I timeout
> +is NULL, the call blocks indefinitely.
> +.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

Please use .__reserved syntax for struct members (also with other
members).

> +must be 0
> +and
> +.I flags
> +must contain one of
> +.BR FUTEX2_SIZE_*
> +ORed with some of the flags below.

I expect the flags should start immediately after 'below'.

> +.P
> +C programs should assign to
> +.I uaddr
> +by casting a pointer to
> +.B uintptr_t
> +to ensure the top bits are cleared on 32-bit systems.

I'm not sure the paragraph above is correct.
What code are you worried about exactly?  Could you show an example?

> +.TP
> +.BR FUTEX2_SIZE_U32
> +.I val
> +and
> +.I *uaddr
> +are 32-bit unsigned integers.
> +.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 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.
> +.\" commit cec199c5e39bde7191a08087cc3d002ccfab31ff
> +.\" Author: Peter Zijlstra <peterz@infradead.org>
> +.\" Date:   Wed Apr 16 18:29:16 2025 +0200
> +.\"
> +.\"     futex: Implement FUTEX2_NUMA
> +.\"
> +.\" FUTEX2_MPOL is not documented or used anywhere;
> +.\" it's unclear to me what it does
> +.\" (defined in commit c042c505210dc3453f378df432c10fff3d471bc5
> +.\"  "futex: Implement FUTEX2_MPOL")
> +.TP
> +.B FUTEX2_PRIVATE
> +By default, the futex is shared
> +.RB "(like " 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.
> +.\"
> +.\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
> +.\"
> +.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
> +was not NULL and did not point to a valid user-space address.
> +.TP
> +.B EFAULT
> +Any
> +.IR waiters []. uaddr
> +field is not a valid user-space address.

Please homogenize the wording of this and the first EFAULT.
(I don't have a preference for which language is best).

> +.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
> +.BR FUTEX2_SIZE_* .
> +.TP
> +.B EINVAL
> +.I flags
> +was not 0.
> +.TP
> +.B EINVAL
> +.I nr_futexes
> +was not in
> +[1,
> +.B FUTEX_WAITV_MAX
> +(128)].
> +.TP
> +.B EINVAL
> +.I timeout
> +was not NULL and
> +.I clockid
> +was not a valid clock
> +.RB ( CLOCK_MONOTONIC
> +or
> +.BR CLOCK_REALTIME ).
> +.TP
> +.B EINVAL
> +.I *timeout
> +is denormal (before epoch or
> +.I tv_nsec
> +more than 999'999'999).

' should actually be \[aq].

> +.TP
> +.B EINVAL
> +Any
> +.IR waiters []. flags
> +field contains an unknown flag.
> +.TP
> +.B EINVAL
> +Any
> +.IR waiters []. flags
> +field is missing a
> +.B FUTEX2_SIZE_*
> +flag or has a size flag different than
> +.BR FUTEX2_SIZE_U32
> +set.
> +.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 index of the biggest NUMA domain
> +(for example,
> +.B FUTEX2_SIZE_U8
> +and there are more than 255 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
> +.I timeout
> +was not NULL and no futex was woken before the timeout elapsed.
> +.TP
> +.BR EAGAIN " or " EWOULDBLOCK
> +The value pointed to by
> +.I uaddr
> +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 NOTES
> +.BR FUTEX2_SIZE_U8 ,
> +.BR FUTEX2_SIZE_U16 ,
> +and
> +.B FUTEX2_SIZE_U64
> +where
> +.I val
> +and
> +.I *uaddr
> +are 8, 16, or 64 bits are defined, but not implemented
> +.RB ( EINVAL ).
> +.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 ().
> +.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!
> +Connection timed out
> +.EE
> +.P
> +.\" SRC BEGIN (futex_waitv.c)
> +.EX
> +#include <errno.h>
> +#include <inttypes.h>
> +#include <linux/futex.h>
> +#include <pthread.h>
> +#include <stdatomic.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_wait_private(_Atomic uint32_t  *uaddr, uint32_t  val,

Should this be atomic?  If so, should we document futex(2) as taking
a pointer to atomic element?

> +                      const struct timespec *_Nullable  timeout)

The two spaces are for variables, but not for function parameters.

Also, my bad: this shouldn't have _Nullable.  We don't use it in
actual code (because Clang's _Nullable is really broken, and only serves
documentation purposes).  Actually, Clang is still sub-par in many ways;
it's a C++ compiler, after all, where the C part is just a side-effect.

> +{
> +	return syscall(SYS_futex, uaddr, FUTEX_WAKE_PRIVATE, val, timeout);
> +}
> +\&
> +static inline long
> +my_futex_waitv(struct futex_waitv  *waiters, unsigned int  nr_futexes,
> +               unsigned int  flags, const struct timespec *_Nullable  timeout,
> +               clockid_t  clockid)
> +{
> +	return syscall(SYS_futex_waitv, waiters, nr_futexes, flags, timeout, clockid);
> +}
> +\&
> +void *
> +worker(void  *arg)
> +{
> +	_Atomic uint32_t  *futex = arg;
> +\&
> +	usleep(*futex * 10000);
> +	*futex *= 2;
> +	my_futex_wait_private(futex, 1, NULL);
> +	return NULL;
> +}
> +\&
> +int
> +main(void)
> +{
> +	_Atomic uint32_t  futexes[10];
> +	uint8_t  init[countof(futexes)];
> +	struct futex_waitv waiters[countof(futexes)] = {};
> +	int  i;
> +\&
> +	getentropy(init, sizeof(init));
> +	init[0] = init[1] = init[2];
> +	for (i = 0; i < countof(futexes); ++i) {
> +		printf("%" PRIu8 "\[rs]t", init[i]);
> +		atomic_init(&futexes[i], init[i]);
> +		pthread_create(&(pthread_t){}, NULL, worker, &futexes[i]);
> +	}
> +	putchar('\[rs]n');
> +\&
> +	for (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 (;;) {
> +		struct timespec  timeout;
> +		int  woke;
> +\&
> +		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))
> +			break;
> +\&
> +		for (i = 0; i < countof(futexes); ++i) {
> +			if (futexes[i] != waiters[i].val)
> +				printf("%" PRIu32 "%s", futexes[i], i == woke ? "!" : "");
> +			putchar('\[rs]t');
> +		}
> +		putchar('\[rs]n');
> +\&
> +		for (i = 0; i < countof(futexes); ++i)
> +			waiters[i].val = futexes[i];
> +	}
> +	fprintf(stderr, "%s\[rs]n", strerror(errno));
> +}
> +.EE
> +.\" SRC END
> +.SH SEE ALSO
> +.ad l

What's the effect of this ad l, and why do we want it?

> +.BR futex (2),
> +.BR FUTEX_WAIT (2const),
> +.BR FUTEX_WAKE (2const),
> +.BR futex (7)
> +.P
> +The following kernel source files:
> +.IP \[bu]
> +.I Documentation/userspace-api/futex2.rst

I'd remove the below, and only point to the rst.


Have a lovely day!
Alex

> +.IP \[bu]
> +.I kernel/futex/syscall.c
> +.IP \[bu]
> +.I kernel/futex/waitwake.c
> +.IP \[bu]
> +.I kernel/futex/futex.h
> diff --git u/man/man7/futex.7 p/man/man7/futex.7
> index 51c5d5d9b..d271144ff 100644
> --- u/man/man7/futex.7
> +++ p/man/man7/futex.7
> @@ -45,7 +45,9 @@ .SS Semantics
>  Any futex operation starts in user space,
>  but it may be necessary to communicate with the kernel using the
>  .BR futex (2)
> -system call.
> +or
> +.BR futex_waitv (2)
> +system calls.
>  .P
>  To "up" a futex, execute the proper assembler instructions that
>  will cause the host CPU to atomically increment the integer.
> @@ -72,7 +74,9 @@ .SS Semantics
>  .P
>  The
>  .BR futex (2)
> -system call can optionally be passed a timeout specifying how long
> +and
> +.BR futex_waitv (2)
> +system calls can optionally be passed a timeout specifying how long
>  the kernel should
>  wait for the futex to be upped.
>  In this case, semantics are more complex and the programmer is referred
> @@ -107,6 +111,7 @@ .SH NOTES
>  .SH SEE ALSO
>  .BR clone (2),
>  .BR futex (2),
> +.BR futex_waitv (2),
>  .BR get_robust_list (2),
>  .BR set_robust_list (2),
>  .BR set_tid_address (2),
> -- 
> 2.39.5



-- 
<https://www.alejandro-colomar.es>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [PATCH v5] futex_waitv.2: new page
  2026-02-11 13:23           ` Alejandro Colomar
@ 2026-02-11 13:51             ` наб
  2026-02-11 14:15               ` [PATCH v6] " наб
  2026-02-11 14:28               ` [PATCH v5] " Alejandro Colomar
  0 siblings, 2 replies; 53+ messages in thread
From: наб @ 2026-02-11 13:51 UTC (permalink / raw)
  To: Alejandro Colomar; +Cc: linux-man

[-- Attachment #1: Type: text/plain, Size: 13321 bytes --]

On Wed, Feb 11, 2026 at 02:23:13PM +0100, Alejandro Colomar wrote:
> On 2026-02-11T05:00:26+0100, наб wrote:
> > +.P
> > +C programs should assign to
> > +.I uaddr
> > +by casting a pointer to
> > +.B uintptr_t
> > +to ensure the top bits are cleared on 32-bit systems.
> 
> I'm not sure the paragraph above is correct.
> What code are you worried about exactly?  Could you show an example?

I couldn't because I repeat this kernel recommendation verbatim here.
I don't think this is an issue, I don't think there's an ILP32 universe
where a pointer-to-u64 assignment fails as described.
I'll just cut it off at _t.
 
> > +static inline long
> > +my_futex_wait_private(_Atomic uint32_t  *uaddr, uint32_t  val,
> 
> Should this be atomic?  If so, should we document futex(2) as taking
> a pointer to atomic element?
It needs to be _Atomic-tagged to match the caller type
(compare passing a const uint32_t to a uint32_t *).
futexes are u32s accessed atomically, which we document everywhere.

Scissor-patch below.

Best,
-- >8 --
From d221a28a34645cf1d3aab7a962de1c755d500841 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= <nabijaczleweli@nabijaczleweli.xyz>
Date: Tue, 10 Feb 2026 21:32:19 +0100
Subject: [PATCH v5] futex_waitv.2: new page
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
X-Mutt-PGP: OS
To: Alejandro Colomar <alx@kernel.org>
Cc: <linux-man@vger.kernel.org>

Signed-off-by: Ahelenia Ziemiańska <nabijaczleweli@nabijaczleweli.xyz>
---
 man/man2/futex_waitv.2 | 419 +++++++++++++++++++++++++++++++++++++++++
 man/man7/futex.7       |   9 +-
 2 files changed, 426 insertions(+), 2 deletions(-)
 create mode 100644 man/man2/futex_waitv.2

diff --git u/man/man2/futex_waitv.2 p/man/man2/futex_waitv.2
new file mode 100644
index 000000000..261cb010c
--- /dev/null
+++ p/man/man2/futex_waitv.2
@@ -0,0 +1,419 @@
+.\" Copyright, the authors of the Linux man-pages project
+.\"
+.\" SPDX-License-Identifier: MIT
+.\"
+.TH futex_waitv 2 (date) "Linux man-pages (unreleased)"
+.SH NAME
+futex_waitv \- wait for FUTEX_WAKE operation on multiple 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 pointed to by the addresses
+.IR waiters []. uaddr
+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 not NULL,
+.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.
+If
+.I timeout
+is NULL, the call blocks indefinitely.
+.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 one of
+.BR FUTEX2_SIZE_*
+ORed with some of the flags below.
+.TP
+.BR FUTEX2_SIZE_U32
+.I .val
+and
+.I *.uaddr
+are 32-bit unsigned integers.
+.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 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.
+.\" commit cec199c5e39bde7191a08087cc3d002ccfab31ff
+.\" Author: Peter Zijlstra <peterz@infradead.org>
+.\" Date:   Wed Apr 16 18:29:16 2025 +0200
+.\"
+.\"     futex: Implement FUTEX2_NUMA
+.\"
+.\" FUTEX2_MPOL is not documented or used anywhere;
+.\" it's unclear to me what it does
+.\" (defined in commit c042c505210dc3453f378df432c10fff3d471bc5
+.\"  "futex: Implement FUTEX2_MPOL")
+.TP
+.B FUTEX2_PRIVATE
+By default, the futex is shared
+.RB "(like " 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
+C programs should assign to
+.I .uaddr
+by casting a pointer to
+.BR 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
+.BR FUTEX2_SIZE_* .
+.TP
+.B EINVAL
+.I flags
+was not 0.
+.TP
+.B EINVAL
+.I n
+was not in
+[1,
+.B FUTEX_WAITV_MAX
+(128)].
+.TP
+.B EINVAL
+.I timeout
+was not NULL and
+.I clockid
+was not a valid clock
+.RB ( CLOCK_MONOTONIC
+or
+.BR CLOCK_REALTIME ).
+.TP
+.B EINVAL
+.I *timeout
+is denormal (before epoch or
+.I tv_nsec
+more than 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 is missing a
+.B FUTEX2_SIZE_*
+flag or has a size flag different than
+.BR FUTEX2_SIZE_U32
+set.
+.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 index of the biggest NUMA domain
+(for example,
+.B FUTEX2_SIZE_U8
+and there are more than 255 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
+.I timeout
+was not NULL and no futex was woken before the timeout elapsed.
+.TP
+.BR EAGAIN " or " EWOULDBLOCK
+The value pointed to by
+.I .uaddr
+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 NOTES
+.BR FUTEX2_SIZE_U8 ,
+.BR FUTEX2_SIZE_U16 ,
+and
+.B FUTEX2_SIZE_U64
+where
+.I .val
+and
+.I *.uaddr
+are 8, 16, or 64 bits are defined, but not implemented
+.RB ( EINVAL ).
+.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 ().
+.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!
+Connection timed out
+.EE
+.P
+.\" SRC BEGIN (futex_waitv.c)
+.EX
+#include <errno.h>
+#include <inttypes.h>
+#include <linux/futex.h>
+#include <pthread.h>
+#include <stdatomic.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_wait_private(_Atomic uint32_t *uaddr, uint32_t val,
+                      const struct timespec *timeout)
+{
+	return syscall(SYS_futex, uaddr, FUTEX_WAKE_PRIVATE, val, timeout);
+}
+\&
+static inline long
+my_futex_waitv(struct futex_waitv *waiters, 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, NULL);
+	return NULL;
+}
+\&
+int
+main(void)
+{
+	_Atomic uint32_t  futexes[10];
+	uint8_t  init[countof(futexes)];
+	struct futex_waitv waiters[countof(futexes)] = {};
+	int  i;
+\&
+	getentropy(init, sizeof(init));
+	init[0] = init[1] = init[2];
+	for (i = 0; i < countof(futexes); ++i) {
+		printf("%" PRIu8 "\[rs]t", init[i]);
+		atomic_init(&futexes[i], init[i]);
+		pthread_create(&(pthread_t){}, NULL, worker, &futexes[i]);
+	}
+	putchar('\[rs]n');
+\&
+	for (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 (;;) {
+		struct timespec  timeout;
+		int  woke;
+\&
+		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))
+			break;
+\&
+		for (i = 0; i < countof(futexes); ++i) {
+			if (futexes[i] != waiters[i].val)
+				printf("%" PRIu32 "%s", futexes[i], i == woke ? "!" : "");
+			putchar('\[rs]t');
+		}
+		putchar('\[rs]n');
+\&
+		for (i = 0; i < countof(futexes); ++i)
+			waiters[i].val = futexes[i];
+	}
+	fprintf(stderr, "%s\[rs]n", strerror(errno));
+}
+.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
diff --git u/man/man7/futex.7 p/man/man7/futex.7
index 51c5d5d9b..d271144ff 100644
--- u/man/man7/futex.7
+++ p/man/man7/futex.7
@@ -45,7 +45,9 @@ .SS Semantics
 Any futex operation starts in user space,
 but it may be necessary to communicate with the kernel using the
 .BR futex (2)
-system call.
+or
+.BR futex_waitv (2)
+system calls.
 .P
 To "up" a futex, execute the proper assembler instructions that
 will cause the host CPU to atomically increment the integer.
@@ -72,7 +74,9 @@ .SS Semantics
 .P
 The
 .BR futex (2)
-system call can optionally be passed a timeout specifying how long
+and
+.BR futex_waitv (2)
+system calls can optionally be passed a timeout specifying how long
 the kernel should
 wait for the futex to be upped.
 In this case, semantics are more complex and the programmer is referred
@@ -107,6 +111,7 @@ .SH NOTES
 .SH SEE ALSO
 .BR clone (2),
 .BR futex (2),
+.BR futex_waitv (2),
 .BR get_robust_list (2),
 .BR set_robust_list (2),
 .BR set_tid_address (2),
-- 
2.39.5


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v6] futex_waitv.2: new page
  2026-02-11 13:51             ` [PATCH v5] " наб
@ 2026-02-11 14:15               ` наб
  2026-02-11 14:31                 ` Alejandro Colomar
  2026-02-11 14:28               ` [PATCH v5] " Alejandro Colomar
  1 sibling, 1 reply; 53+ messages in thread
From: наб @ 2026-02-11 14:15 UTC (permalink / raw)
  To: Alejandro Colomar; +Cc: linux-man

[-- Attachment #1: Type: text/plain, Size: 11865 bytes --]

Signed-off-by: Ahelenia Ziemiańska <nabijaczleweli@nabijaczleweli.xyz>
---
Same as v5 but FUTEX_WAKE_PRIVATE is no longer given a timeout argument
it doesn't use.

 man/man2/futex_waitv.2 | 418 +++++++++++++++++++++++++++++++++++++++++
 man/man7/futex.7       |   9 +-
 2 files changed, 425 insertions(+), 2 deletions(-)
 create mode 100644 man/man2/futex_waitv.2

diff --git u/man/man2/futex_waitv.2 p/man/man2/futex_waitv.2
new file mode 100644
index 000000000..bc6f7528c
--- /dev/null
+++ p/man/man2/futex_waitv.2
@@ -0,0 +1,418 @@
+.\" Copyright, the authors of the Linux man-pages project
+.\"
+.\" SPDX-License-Identifier: MIT
+.\"
+.TH futex_waitv 2 (date) "Linux man-pages (unreleased)"
+.SH NAME
+futex_waitv \- wait for FUTEX_WAKE operation on multiple 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 pointed to by the addresses
+.IR waiters []. uaddr
+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 not NULL,
+.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.
+If
+.I timeout
+is NULL, the call blocks indefinitely.
+.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 one of
+.BR FUTEX2_SIZE_*
+ORed with some of the flags below.
+.TP
+.BR FUTEX2_SIZE_U32
+.I .val
+and
+.I *.uaddr
+are 32-bit unsigned integers.
+.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 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.
+.\" commit cec199c5e39bde7191a08087cc3d002ccfab31ff
+.\" Author: Peter Zijlstra <peterz@infradead.org>
+.\" Date:   Wed Apr 16 18:29:16 2025 +0200
+.\"
+.\"     futex: Implement FUTEX2_NUMA
+.\"
+.\" FUTEX2_MPOL is not documented or used anywhere;
+.\" it's unclear to me what it does
+.\" (defined in commit c042c505210dc3453f378df432c10fff3d471bc5
+.\"  "futex: Implement FUTEX2_MPOL")
+.TP
+.B FUTEX2_PRIVATE
+By default, the futex is shared
+.RB "(like " 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
+C programs should assign to
+.I .uaddr
+by casting a pointer to
+.BR 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
+.BR FUTEX2_SIZE_* .
+.TP
+.B EINVAL
+.I flags
+was not 0.
+.TP
+.B EINVAL
+.I n
+was not in
+[1,
+.B FUTEX_WAITV_MAX
+(128)].
+.TP
+.B EINVAL
+.I timeout
+was not NULL and
+.I clockid
+was not a valid clock
+.RB ( CLOCK_MONOTONIC
+or
+.BR CLOCK_REALTIME ).
+.TP
+.B EINVAL
+.I *timeout
+is denormal (before epoch or
+.I tv_nsec
+more than 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 is missing a
+.B FUTEX2_SIZE_*
+flag or has a size flag different than
+.BR FUTEX2_SIZE_U32
+set.
+.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 index of the biggest NUMA domain
+(for example,
+.B FUTEX2_SIZE_U8
+and there are more than 255 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
+.I timeout
+was not NULL and no futex was woken before the timeout elapsed.
+.TP
+.BR EAGAIN " or " EWOULDBLOCK
+The value pointed to by
+.I .uaddr
+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 NOTES
+.BR FUTEX2_SIZE_U8 ,
+.BR FUTEX2_SIZE_U16 ,
+and
+.B FUTEX2_SIZE_U64
+where
+.I .val
+and
+.I *.uaddr
+are 8, 16, or 64 bits are defined, but not implemented
+.RB ( EINVAL ).
+.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 ().
+.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!
+Connection timed out
+.EE
+.P
+.\" SRC BEGIN (futex_waitv.c)
+.EX
+#include <errno.h>
+#include <inttypes.h>
+#include <linux/futex.h>
+#include <pthread.h>
+#include <stdatomic.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_wait_private(_Atomic uint32_t *uaddr, uint32_t val)
+{
+	return syscall(SYS_futex, uaddr, FUTEX_WAKE_PRIVATE, val);
+}
+\&
+static inline long
+my_futex_waitv(struct futex_waitv *waiters, 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)] = {};
+	int  i;
+\&
+	getentropy(init, sizeof(init));
+	init[0] = init[1] = init[2];
+	for (i = 0; i < countof(futexes); ++i) {
+		printf("%" PRIu8 "\[rs]t", init[i]);
+		atomic_init(&futexes[i], init[i]);
+		pthread_create(&(pthread_t){}, NULL, worker, &futexes[i]);
+	}
+	putchar('\[rs]n');
+\&
+	for (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 (;;) {
+		struct timespec  timeout;
+		int  woke;
+\&
+		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))
+			break;
+\&
+		for (i = 0; i < countof(futexes); ++i) {
+			if (futexes[i] != waiters[i].val)
+				printf("%" PRIu32 "%s", futexes[i], i == woke ? "!" : "");
+			putchar('\[rs]t');
+		}
+		putchar('\[rs]n');
+\&
+		for (i = 0; i < countof(futexes); ++i)
+			waiters[i].val = futexes[i];
+	}
+	fprintf(stderr, "%s\[rs]n", strerror(errno));
+}
+.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
diff --git u/man/man7/futex.7 p/man/man7/futex.7
index 51c5d5d9b..d271144ff 100644
--- u/man/man7/futex.7
+++ p/man/man7/futex.7
@@ -45,7 +45,9 @@ .SS Semantics
 Any futex operation starts in user space,
 but it may be necessary to communicate with the kernel using the
 .BR futex (2)
-system call.
+or
+.BR futex_waitv (2)
+system calls.
 .P
 To "up" a futex, execute the proper assembler instructions that
 will cause the host CPU to atomically increment the integer.
@@ -72,7 +74,9 @@ .SS Semantics
 .P
 The
 .BR futex (2)
-system call can optionally be passed a timeout specifying how long
+and
+.BR futex_waitv (2)
+system calls can optionally be passed a timeout specifying how long
 the kernel should
 wait for the futex to be upped.
 In this case, semantics are more complex and the programmer is referred
@@ -107,6 +111,7 @@ .SH NOTES
 .SH SEE ALSO
 .BR clone (2),
 .BR futex (2),
+.BR futex_waitv (2),
 .BR get_robust_list (2),
 .BR set_robust_list (2),
 .BR set_tid_address (2),
-- 
2.39.5

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* Re: [PATCH v5] futex_waitv.2: new page
  2026-02-11 13:51             ` [PATCH v5] " наб
  2026-02-11 14:15               ` [PATCH v6] " наб
@ 2026-02-11 14:28               ` Alejandro Colomar
  1 sibling, 0 replies; 53+ messages in thread
From: Alejandro Colomar @ 2026-02-11 14:28 UTC (permalink / raw)
  To: наб; +Cc: linux-man

[-- Attachment #1: Type: text/plain, Size: 17995 bytes --]

Hi,

On 2026-02-11T14:51:27+0100, наб wrote:
> On Wed, Feb 11, 2026 at 02:23:13PM +0100, Alejandro Colomar wrote:
> > On 2026-02-11T05:00:26+0100, наб wrote:
> > > +.P
> > > +C programs should assign to
> > > +.I uaddr
> > > +by casting a pointer to
> > > +.B uintptr_t
> > > +to ensure the top bits are cleared on 32-bit systems.
> > 
> > I'm not sure the paragraph above is correct.
> > What code are you worried about exactly?  Could you show an example?
> 
> I couldn't because I repeat this kernel recommendation verbatim here.

Heh.  Kernel people are funny sometimes.  :)

> I don't think this is an issue, I don't think there's an ILP32 universe
> where a pointer-to-u64 assignment fails as described.
> I'll just cut it off at _t.

Agree.

>  
> > > +static inline long
> > > +my_futex_wait_private(_Atomic uint32_t  *uaddr, uint32_t  val,
> > 
> > Should this be atomic?  If so, should we document futex(2) as taking
> > a pointer to atomic element?
> It needs to be _Atomic-tagged to match the caller type
> (compare passing a const uint32_t to a uint32_t *).

Makes sense.

> futexes are u32s accessed atomically, which we document everywhere.

Okay.

> 
> Scissor-patch below.

I found a few things I'd improve.  Please apply these changes.  I'd like
to look at it again before merging, so please send a revision.

	diff --git i/man/man2/futex_waitv.2 w/man/man2/futex_waitv.2
	index d2997a94bf92..555dd42c6598 100644
	--- i/man/man2/futex_waitv.2
	+++ w/man/man2/futex_waitv.2
	@@ -76,25 +76,27 @@ .SH DESCRIPTION
	 and is guaranteed not to expire early.
	 If
	 .I timeout
	-is NULL, the call blocks indefinitely.
	+is NULL,
	+the call may block indefinitely.
	 .P

(semantic newlines)

	 Futex words to monitor are given by
	 .IR "struct futex_waitv" ,
	 whose fields are analogous to
	 .BR FUTEX_WAIT (2const)
	-parameters, except
	+parameters,
	+except
	 .I .__reserved
	 must be 0
	 and
	 .I .flags
	 must contain one of
	-.BR FUTEX2_SIZE_*
	+.BI FUTEX2_SIZE_ *
	 ORed with some of the flags below.

* is variable part, so it should be in italics.

	 .TP
	 .BR FUTEX2_SIZE_U32
	 .I .val
	 and
	-.I *.uaddr
	+.I .uaddr[]
	 are 32-bit unsigned integers.

[] better represents that it's an array.

	 .TP
	 .B FUTEX2_NUMA
	@@ -139,7 +141,8 @@ .SH DESCRIPTION
	 .TP
	 .B FUTEX2_PRIVATE
	 By default, the futex is shared
	-.RB "(like " FUTEX_WAIT (2const)),
	+(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
	@@ -148,7 +151,7 @@ .SH DESCRIPTION
	 this most often means they are part of the same process).
	 Private futexes are faster than shared ones.
	 .P
	-C programs should assign to
	+Programs should assign to
	 .I .uaddr
	 by casting a pointer to
	 .BR uintptr_t .

We assume all programs are C programs, or similar enough.

	@@ -189,7 +192,7 @@ .SH ERRORS
	 .IR waiters []. uaddr
	 field does not point to a valid object\[em]that is,
	 the address is not aligned appropriately for the specified
	-.BR FUTEX2_SIZE_* .
	+.BR FUTEX2_SIZE_\f[I]*\f[] .
	 .TP
	 .B EINVAL
	 .I flags
	@@ -197,10 +200,9 @@ .SH ERRORS
	 .TP
	 .B EINVAL
	 .I n
	-was not in
	-[1,
	-.B FUTEX_WAITV_MAX
	-(128)].
	+was not in the range
	+.RB [ 1 ,
	+.IR FUTEX_WAITV_MAX(128) ].

This is an expression, so it should be in italics.  1 is a constant, so
bold.

	 .TP
	 .B EINVAL
	 .I timeout
	@@ -226,7 +228,7 @@ .SH ERRORS
	 Any
	 .IR waiters []. flags
	 field is missing a
	-.B FUTEX2_SIZE_*
	+.BI FUTEX2_SIZE_ *
	 flag or has a size flag different than
	 .BR FUTEX2_SIZE_U32
	 set.
	@@ -287,7 +289,7 @@ .SH NOTES
	 where
	 .I .val
	 and
	-.I *.uaddr
	+.I .uaddr[]
	 are 8, 16, or 64 bits are defined, but not implemented
	 .RB ( EINVAL ).
	 .SH HISTORY
	@@ -328,6 +330,7 @@ .SH EXAMPLES
	 #include <linux/futex.h>
	 #include <pthread.h>
	 #include <stdatomic.h>
	+#include <stdcountof.h>
	 #include <stdint.h>
	 #include <stdio.h>
	 #include <stdlib.h>

<stdcountof.h> provides countof(3).

	@@ -344,7 +347,8 @@ .SH EXAMPLES
	 }
	 \&
	 static inline long
	-my_futex_waitv(struct futex_waitv *waiters, unsigned int n,
	+my_futex_waitv(unsigned int n;
	+               struct futex_waitv waiters[n], unsigned int n,
			unsigned int flags, const struct timespec *timeout,
			clockid_t clockid)

Let's use actual array notation, as GCC supports it.  Clang is too bad,
and either it should improve, or people should stop considering Clang
a serious C compiler.


Have a lovely day!
Alex

	 {

> 
> Best,
> -- >8 --
> From d221a28a34645cf1d3aab7a962de1c755d500841 Mon Sep 17 00:00:00 2001
> From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= <nabijaczleweli@nabijaczleweli.xyz>
> Date: Tue, 10 Feb 2026 21:32:19 +0100
> Subject: [PATCH v5] futex_waitv.2: new page
> MIME-Version: 1.0
> Content-Type: text/plain; charset=UTF-8
> Content-Transfer-Encoding: 8bit
> X-Mutt-PGP: OS
> To: Alejandro Colomar <alx@kernel.org>
> Cc: <linux-man@vger.kernel.org>
> 
> Signed-off-by: Ahelenia Ziemiańska <nabijaczleweli@nabijaczleweli.xyz>
> ---
>  man/man2/futex_waitv.2 | 419 +++++++++++++++++++++++++++++++++++++++++
>  man/man7/futex.7       |   9 +-
>  2 files changed, 426 insertions(+), 2 deletions(-)
>  create mode 100644 man/man2/futex_waitv.2
> 
> diff --git u/man/man2/futex_waitv.2 p/man/man2/futex_waitv.2
> new file mode 100644
> index 000000000..261cb010c
> --- /dev/null
> +++ p/man/man2/futex_waitv.2
> @@ -0,0 +1,419 @@
> +.\" Copyright, the authors of the Linux man-pages project
> +.\"
> +.\" SPDX-License-Identifier: MIT
> +.\"
> +.TH futex_waitv 2 (date) "Linux man-pages (unreleased)"
> +.SH NAME
> +futex_waitv \- wait for FUTEX_WAKE operation on multiple 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 pointed to by the addresses
> +.IR waiters []. uaddr
> +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 not NULL,
> +.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.
> +If
> +.I timeout
> +is NULL, the call blocks indefinitely.
> +.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 one of
> +.BR FUTEX2_SIZE_*
> +ORed with some of the flags below.
> +.TP
> +.BR FUTEX2_SIZE_U32
> +.I .val
> +and
> +.I *.uaddr
> +are 32-bit unsigned integers.
> +.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 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.
> +.\" commit cec199c5e39bde7191a08087cc3d002ccfab31ff
> +.\" Author: Peter Zijlstra <peterz@infradead.org>
> +.\" Date:   Wed Apr 16 18:29:16 2025 +0200
> +.\"
> +.\"     futex: Implement FUTEX2_NUMA
> +.\"
> +.\" FUTEX2_MPOL is not documented or used anywhere;
> +.\" it's unclear to me what it does
> +.\" (defined in commit c042c505210dc3453f378df432c10fff3d471bc5
> +.\"  "futex: Implement FUTEX2_MPOL")
> +.TP
> +.B FUTEX2_PRIVATE
> +By default, the futex is shared
> +.RB "(like " 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
> +C programs should assign to
> +.I .uaddr
> +by casting a pointer to
> +.BR 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
> +.BR FUTEX2_SIZE_* .
> +.TP
> +.B EINVAL
> +.I flags
> +was not 0.
> +.TP
> +.B EINVAL
> +.I n
> +was not in
> +[1,
> +.B FUTEX_WAITV_MAX
> +(128)].
> +.TP
> +.B EINVAL
> +.I timeout
> +was not NULL and
> +.I clockid
> +was not a valid clock
> +.RB ( CLOCK_MONOTONIC
> +or
> +.BR CLOCK_REALTIME ).
> +.TP
> +.B EINVAL
> +.I *timeout
> +is denormal (before epoch or
> +.I tv_nsec
> +more than 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 is missing a
> +.B FUTEX2_SIZE_*
> +flag or has a size flag different than
> +.BR FUTEX2_SIZE_U32
> +set.
> +.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 index of the biggest NUMA domain
> +(for example,
> +.B FUTEX2_SIZE_U8
> +and there are more than 255 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
> +.I timeout
> +was not NULL and no futex was woken before the timeout elapsed.
> +.TP
> +.BR EAGAIN " or " EWOULDBLOCK
> +The value pointed to by
> +.I .uaddr
> +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 NOTES
> +.BR FUTEX2_SIZE_U8 ,
> +.BR FUTEX2_SIZE_U16 ,
> +and
> +.B FUTEX2_SIZE_U64
> +where
> +.I .val
> +and
> +.I *.uaddr
> +are 8, 16, or 64 bits are defined, but not implemented
> +.RB ( EINVAL ).
> +.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 ().
> +.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!
> +Connection timed out
> +.EE
> +.P
> +.\" SRC BEGIN (futex_waitv.c)
> +.EX
> +#include <errno.h>
> +#include <inttypes.h>
> +#include <linux/futex.h>
> +#include <pthread.h>
> +#include <stdatomic.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_wait_private(_Atomic uint32_t *uaddr, uint32_t val,
> +                      const struct timespec *timeout)
> +{
> +	return syscall(SYS_futex, uaddr, FUTEX_WAKE_PRIVATE, val, timeout);
> +}
> +\&
> +static inline long
> +my_futex_waitv(struct futex_waitv *waiters, 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, NULL);
> +	return NULL;
> +}
> +\&
> +int
> +main(void)
> +{
> +	_Atomic uint32_t  futexes[10];
> +	uint8_t  init[countof(futexes)];
> +	struct futex_waitv waiters[countof(futexes)] = {};
> +	int  i;
> +\&
> +	getentropy(init, sizeof(init));
> +	init[0] = init[1] = init[2];
> +	for (i = 0; i < countof(futexes); ++i) {
> +		printf("%" PRIu8 "\[rs]t", init[i]);
> +		atomic_init(&futexes[i], init[i]);
> +		pthread_create(&(pthread_t){}, NULL, worker, &futexes[i]);
> +	}
> +	putchar('\[rs]n');
> +\&
> +	for (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 (;;) {
> +		struct timespec  timeout;
> +		int  woke;
> +\&
> +		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))
> +			break;
> +\&
> +		for (i = 0; i < countof(futexes); ++i) {
> +			if (futexes[i] != waiters[i].val)
> +				printf("%" PRIu32 "%s", futexes[i], i == woke ? "!" : "");
> +			putchar('\[rs]t');
> +		}
> +		putchar('\[rs]n');
> +\&
> +		for (i = 0; i < countof(futexes); ++i)
> +			waiters[i].val = futexes[i];
> +	}
> +	fprintf(stderr, "%s\[rs]n", strerror(errno));
> +}
> +.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
> diff --git u/man/man7/futex.7 p/man/man7/futex.7
> index 51c5d5d9b..d271144ff 100644
> --- u/man/man7/futex.7
> +++ p/man/man7/futex.7
> @@ -45,7 +45,9 @@ .SS Semantics
>  Any futex operation starts in user space,
>  but it may be necessary to communicate with the kernel using the
>  .BR futex (2)
> -system call.
> +or
> +.BR futex_waitv (2)
> +system calls.
>  .P
>  To "up" a futex, execute the proper assembler instructions that
>  will cause the host CPU to atomically increment the integer.
> @@ -72,7 +74,9 @@ .SS Semantics
>  .P
>  The
>  .BR futex (2)
> -system call can optionally be passed a timeout specifying how long
> +and
> +.BR futex_waitv (2)
> +system calls can optionally be passed a timeout specifying how long
>  the kernel should
>  wait for the futex to be upped.
>  In this case, semantics are more complex and the programmer is referred
> @@ -107,6 +111,7 @@ .SH NOTES
>  .SH SEE ALSO
>  .BR clone (2),
>  .BR futex (2),
> +.BR futex_waitv (2),
>  .BR get_robust_list (2),
>  .BR set_robust_list (2),
>  .BR set_tid_address (2),
> -- 
> 2.39.5
> 



-- 
<https://www.alejandro-colomar.es>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v6] futex_waitv.2: new page
  2026-02-11 14:15               ` [PATCH v6] " наб
@ 2026-02-11 14:31                 ` Alejandro Colomar
  2026-02-11 14:44                   ` [PATCH v7] " наб
  0 siblings, 1 reply; 53+ messages in thread
From: Alejandro Colomar @ 2026-02-11 14:31 UTC (permalink / raw)
  To: наб; +Cc: linux-man

[-- Attachment #1: Type: text/plain, Size: 13057 bytes --]

On 2026-02-11T15:15:35+0100, наб wrote:
> Signed-off-by: Ahelenia Ziemiańska <nabijaczleweli@nabijaczleweli.xyz>
> ---
> Same as v5 but FUTEX_WAKE_PRIVATE is no longer given a timeout argument
> it doesn't use.

I'd appreciate range-diffs to be able to understand what actually
changes in patch revisions.  See <CONTRIBUTING.d/patches/range-diff>.


Cheers,
Alex

> 
>  man/man2/futex_waitv.2 | 418 +++++++++++++++++++++++++++++++++++++++++
>  man/man7/futex.7       |   9 +-
>  2 files changed, 425 insertions(+), 2 deletions(-)
>  create mode 100644 man/man2/futex_waitv.2
> 
> diff --git u/man/man2/futex_waitv.2 p/man/man2/futex_waitv.2
> new file mode 100644
> index 000000000..bc6f7528c
> --- /dev/null
> +++ p/man/man2/futex_waitv.2
> @@ -0,0 +1,418 @@
> +.\" Copyright, the authors of the Linux man-pages project
> +.\"
> +.\" SPDX-License-Identifier: MIT
> +.\"
> +.TH futex_waitv 2 (date) "Linux man-pages (unreleased)"
> +.SH NAME
> +futex_waitv \- wait for FUTEX_WAKE operation on multiple 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 pointed to by the addresses
> +.IR waiters []. uaddr
> +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 not NULL,
> +.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.
> +If
> +.I timeout
> +is NULL, the call blocks indefinitely.
> +.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 one of
> +.BR FUTEX2_SIZE_*
> +ORed with some of the flags below.
> +.TP
> +.BR FUTEX2_SIZE_U32
> +.I .val
> +and
> +.I *.uaddr
> +are 32-bit unsigned integers.
> +.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 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.
> +.\" commit cec199c5e39bde7191a08087cc3d002ccfab31ff
> +.\" Author: Peter Zijlstra <peterz@infradead.org>
> +.\" Date:   Wed Apr 16 18:29:16 2025 +0200
> +.\"
> +.\"     futex: Implement FUTEX2_NUMA
> +.\"
> +.\" FUTEX2_MPOL is not documented or used anywhere;
> +.\" it's unclear to me what it does
> +.\" (defined in commit c042c505210dc3453f378df432c10fff3d471bc5
> +.\"  "futex: Implement FUTEX2_MPOL")
> +.TP
> +.B FUTEX2_PRIVATE
> +By default, the futex is shared
> +.RB "(like " 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
> +C programs should assign to
> +.I .uaddr
> +by casting a pointer to
> +.BR 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
> +.BR FUTEX2_SIZE_* .
> +.TP
> +.B EINVAL
> +.I flags
> +was not 0.
> +.TP
> +.B EINVAL
> +.I n
> +was not in
> +[1,
> +.B FUTEX_WAITV_MAX
> +(128)].
> +.TP
> +.B EINVAL
> +.I timeout
> +was not NULL and
> +.I clockid
> +was not a valid clock
> +.RB ( CLOCK_MONOTONIC
> +or
> +.BR CLOCK_REALTIME ).
> +.TP
> +.B EINVAL
> +.I *timeout
> +is denormal (before epoch or
> +.I tv_nsec
> +more than 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 is missing a
> +.B FUTEX2_SIZE_*
> +flag or has a size flag different than
> +.BR FUTEX2_SIZE_U32
> +set.
> +.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 index of the biggest NUMA domain
> +(for example,
> +.B FUTEX2_SIZE_U8
> +and there are more than 255 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
> +.I timeout
> +was not NULL and no futex was woken before the timeout elapsed.
> +.TP
> +.BR EAGAIN " or " EWOULDBLOCK
> +The value pointed to by
> +.I .uaddr
> +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 NOTES
> +.BR FUTEX2_SIZE_U8 ,
> +.BR FUTEX2_SIZE_U16 ,
> +and
> +.B FUTEX2_SIZE_U64
> +where
> +.I .val
> +and
> +.I *.uaddr
> +are 8, 16, or 64 bits are defined, but not implemented
> +.RB ( EINVAL ).
> +.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 ().
> +.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!
> +Connection timed out
> +.EE
> +.P
> +.\" SRC BEGIN (futex_waitv.c)
> +.EX
> +#include <errno.h>
> +#include <inttypes.h>
> +#include <linux/futex.h>
> +#include <pthread.h>
> +#include <stdatomic.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_wait_private(_Atomic uint32_t *uaddr, uint32_t val)
> +{
> +	return syscall(SYS_futex, uaddr, FUTEX_WAKE_PRIVATE, val);
> +}
> +\&
> +static inline long
> +my_futex_waitv(struct futex_waitv *waiters, 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)] = {};
> +	int  i;
> +\&
> +	getentropy(init, sizeof(init));
> +	init[0] = init[1] = init[2];
> +	for (i = 0; i < countof(futexes); ++i) {
> +		printf("%" PRIu8 "\[rs]t", init[i]);
> +		atomic_init(&futexes[i], init[i]);
> +		pthread_create(&(pthread_t){}, NULL, worker, &futexes[i]);
> +	}
> +	putchar('\[rs]n');
> +\&
> +	for (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 (;;) {
> +		struct timespec  timeout;
> +		int  woke;
> +\&
> +		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))
> +			break;
> +\&
> +		for (i = 0; i < countof(futexes); ++i) {
> +			if (futexes[i] != waiters[i].val)
> +				printf("%" PRIu32 "%s", futexes[i], i == woke ? "!" : "");
> +			putchar('\[rs]t');
> +		}
> +		putchar('\[rs]n');
> +\&
> +		for (i = 0; i < countof(futexes); ++i)
> +			waiters[i].val = futexes[i];
> +	}
> +	fprintf(stderr, "%s\[rs]n", strerror(errno));
> +}
> +.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
> diff --git u/man/man7/futex.7 p/man/man7/futex.7
> index 51c5d5d9b..d271144ff 100644
> --- u/man/man7/futex.7
> +++ p/man/man7/futex.7
> @@ -45,7 +45,9 @@ .SS Semantics
>  Any futex operation starts in user space,
>  but it may be necessary to communicate with the kernel using the
>  .BR futex (2)
> -system call.
> +or
> +.BR futex_waitv (2)
> +system calls.
>  .P
>  To "up" a futex, execute the proper assembler instructions that
>  will cause the host CPU to atomically increment the integer.
> @@ -72,7 +74,9 @@ .SS Semantics
>  .P
>  The
>  .BR futex (2)
> -system call can optionally be passed a timeout specifying how long
> +and
> +.BR futex_waitv (2)
> +system calls can optionally be passed a timeout specifying how long
>  the kernel should
>  wait for the futex to be upped.
>  In this case, semantics are more complex and the programmer is referred
> @@ -107,6 +111,7 @@ .SH NOTES
>  .SH SEE ALSO
>  .BR clone (2),
>  .BR futex (2),
> +.BR futex_waitv (2),
>  .BR get_robust_list (2),
>  .BR set_robust_list (2),
>  .BR set_tid_address (2),
> -- 
> 2.39.5



-- 
<https://www.alejandro-colomar.es>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [PATCH v7] futex_waitv.2: new page
  2026-02-11 14:31                 ` Alejandro Colomar
@ 2026-02-11 14:44                   ` наб
  2026-02-11 14:55                     ` Alejandro Colomar
  2026-02-14 17:32                     ` Alejandro Colomar
  0 siblings, 2 replies; 53+ messages in thread
From: наб @ 2026-02-11 14:44 UTC (permalink / raw)
  To: Alejandro Colomar; +Cc: linux-man

[-- Attachment #1: Type: text/plain, Size: 15002 bytes --]

Signed-off-by: Ahelenia Ziemiańska <nabijaczleweli@nabijaczleweli.xyz>
---
Range-diff against v5:
1:  d221a28a3 ! 1:  da50b4733 futex_waitv.2: new page
    @@ man/man2/futex_waitv.2 (new)
     +and is guaranteed not to expire early.
     +If
     +.I timeout
    -+is NULL, the call blocks indefinitely.
    ++is NULL,
    ++the call blocks indefinitely.
     +.P
     +Futex words to monitor are given by
     +.IR "struct futex_waitv" ,
    @@ man/man2/futex_waitv.2 (new)
     +and
     +.I .flags
     +must contain one of
    -+.BR FUTEX2_SIZE_*
    ++.BI FUTEX2_SIZE_ *
     +ORed with some of the flags below.
     +.TP
    -+.BR FUTEX2_SIZE_U32
    ++.B FUTEX2_SIZE_U32
     +.I .val
     +and
    -+.I *.uaddr
    ++.I .uaddr[]
     +are 32-bit unsigned integers.
     +.TP
     +.B FUTEX2_NUMA
    @@ man/man2/futex_waitv.2 (new)
     +this most often means they are part of the same process).
     +Private futexes are faster than shared ones.
     +.P
    -+C programs should assign to
    ++Programs should assign to
     +.I .uaddr
     +by casting a pointer to
     +.BR uintptr_t .
    @@ man/man2/futex_waitv.2 (new)
     +.IR waiters []. uaddr
     +field does not point to a valid object\[em]that is,
     +the address is not aligned appropriately for the specified
    -+.BR FUTEX2_SIZE_* .
    ++.BI FUTEX2_SIZE_ * .
     +.TP
     +.B EINVAL
     +.I flags
    @@ man/man2/futex_waitv.2 (new)
     +.TP
     +.B EINVAL
     +.I n
    -+was not in
    -+[1,
    -+.B FUTEX_WAITV_MAX
    ++was not in the range
    ++.RB [ 1 ,
    ++.I FUTEX_WAITV_MAX
     +(128)].
     +.TP
     +.B EINVAL
    @@ man/man2/futex_waitv.2 (new)
     +Any
     +.IR waiters []. flags
     +field is missing a
    -+.B FUTEX2_SIZE_*
    ++.BI FUTEX2_SIZE_ *
     +flag or has a size flag different than
    -+.BR FUTEX2_SIZE_U32
    ++.B FUTEX2_SIZE_U32
     +set.
     +.TP
     +.B EINVAL
    @@ man/man2/futex_waitv.2 (new)
     +#include <linux/futex.h>
     +#include <pthread.h>
     +#include <stdatomic.h>
    ++#include <stdcountof.h>
     +#include <stdint.h>
     +#include <stdio.h>
     +#include <stdlib.h>
    @@ man/man2/futex_waitv.2 (new)
     +#include <unistd.h>
     +\&
     +static inline long
    -+my_futex_wait_private(_Atomic uint32_t *uaddr, uint32_t val,
    -+                      const struct timespec *timeout)
    ++my_futex_wait_private(_Atomic uint32_t *uaddr, uint32_t val)
     +{
    -+	return syscall(SYS_futex, uaddr, FUTEX_WAKE_PRIVATE, val, timeout);
    ++	return syscall(SYS_futex, uaddr, FUTEX_WAKE_PRIVATE, val);
     +}
     +\&
     +static inline long
    -+my_futex_waitv(struct futex_waitv *waiters, unsigned int n,
    ++my_futex_waitv(unsigned int n;
    ++               struct futex_waitv waiters[n], unsigned int n,
     +               unsigned int flags, const struct timespec *timeout,
     +               clockid_t clockid)
     +{
    @@ man/man2/futex_waitv.2 (new)
     +\&
     +	usleep(*futex * 10000);
     +	*futex *= 2;
    -+	my_futex_wait_private(futex, 1, NULL);
    ++	my_futex_wait_private(futex, 1);
     +	return NULL;
     +}
     +\&

 man/man2/futex_waitv.2 | 421 +++++++++++++++++++++++++++++++++++++++++
 man/man7/futex.7       |   9 +-
 2 files changed, 428 insertions(+), 2 deletions(-)
 create mode 100644 man/man2/futex_waitv.2

diff --git u/man/man2/futex_waitv.2 p/man/man2/futex_waitv.2
new file mode 100644
index 000000000..a1eeb8ce8
--- /dev/null
+++ p/man/man2/futex_waitv.2
@@ -0,0 +1,421 @@
+.\" Copyright, the authors of the Linux man-pages project
+.\"
+.\" SPDX-License-Identifier: MIT
+.\"
+.TH futex_waitv 2 (date) "Linux man-pages (unreleased)"
+.SH NAME
+futex_waitv \- wait for FUTEX_WAKE operation on multiple 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 pointed to by the addresses
+.IR waiters []. uaddr
+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 not NULL,
+.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.
+If
+.I timeout
+is NULL,
+the call blocks indefinitely.
+.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 one of
+.BI FUTEX2_SIZE_ *
+ORed with some of the flags below.
+.TP
+.B FUTEX2_SIZE_U32
+.I .val
+and
+.I .uaddr[]
+are 32-bit unsigned integers.
+.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 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.
+.\" commit cec199c5e39bde7191a08087cc3d002ccfab31ff
+.\" Author: Peter Zijlstra <peterz@infradead.org>
+.\" Date:   Wed Apr 16 18:29:16 2025 +0200
+.\"
+.\"     futex: Implement FUTEX2_NUMA
+.\"
+.\" FUTEX2_MPOL is not documented or used anywhere;
+.\" it's unclear to me what it does
+.\" (defined in commit c042c505210dc3453f378df432c10fff3d471bc5
+.\"  "futex: Implement FUTEX2_MPOL")
+.TP
+.B FUTEX2_PRIVATE
+By default, the futex is shared
+.RB "(like " 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
+.BR 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 ,
+.I FUTEX_WAITV_MAX
+(128)].
+.TP
+.B EINVAL
+.I timeout
+was not NULL and
+.I clockid
+was not a valid clock
+.RB ( CLOCK_MONOTONIC
+or
+.BR CLOCK_REALTIME ).
+.TP
+.B EINVAL
+.I *timeout
+is denormal (before epoch or
+.I tv_nsec
+more than 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 is missing a
+.BI FUTEX2_SIZE_ *
+flag or has a size flag different than
+.B FUTEX2_SIZE_U32
+set.
+.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 index of the biggest NUMA domain
+(for example,
+.B FUTEX2_SIZE_U8
+and there are more than 255 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
+.I timeout
+was not NULL and no futex was woken before the timeout elapsed.
+.TP
+.BR EAGAIN " or " EWOULDBLOCK
+The value pointed to by
+.I .uaddr
+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 NOTES
+.BR FUTEX2_SIZE_U8 ,
+.BR FUTEX2_SIZE_U16 ,
+and
+.B FUTEX2_SIZE_U64
+where
+.I .val
+and
+.I *.uaddr
+are 8, 16, or 64 bits are defined, but not implemented
+.RB ( EINVAL ).
+.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 ().
+.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!
+Connection timed out
+.EE
+.P
+.\" SRC BEGIN (futex_waitv.c)
+.EX
+#include <errno.h>
+#include <inttypes.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_wait_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)] = {};
+	int  i;
+\&
+	getentropy(init, sizeof(init));
+	init[0] = init[1] = init[2];
+	for (i = 0; i < countof(futexes); ++i) {
+		printf("%" PRIu8 "\[rs]t", init[i]);
+		atomic_init(&futexes[i], init[i]);
+		pthread_create(&(pthread_t){}, NULL, worker, &futexes[i]);
+	}
+	putchar('\[rs]n');
+\&
+	for (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 (;;) {
+		struct timespec  timeout;
+		int  woke;
+\&
+		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))
+			break;
+\&
+		for (i = 0; i < countof(futexes); ++i) {
+			if (futexes[i] != waiters[i].val)
+				printf("%" PRIu32 "%s", futexes[i], i == woke ? "!" : "");
+			putchar('\[rs]t');
+		}
+		putchar('\[rs]n');
+\&
+		for (i = 0; i < countof(futexes); ++i)
+			waiters[i].val = futexes[i];
+	}
+	fprintf(stderr, "%s\[rs]n", strerror(errno));
+}
+.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
diff --git u/man/man7/futex.7 p/man/man7/futex.7
index 51c5d5d9b..d271144ff 100644
--- u/man/man7/futex.7
+++ p/man/man7/futex.7
@@ -45,7 +45,9 @@ .SS Semantics
 Any futex operation starts in user space,
 but it may be necessary to communicate with the kernel using the
 .BR futex (2)
-system call.
+or
+.BR futex_waitv (2)
+system calls.
 .P
 To "up" a futex, execute the proper assembler instructions that
 will cause the host CPU to atomically increment the integer.
@@ -72,7 +74,9 @@ .SS Semantics
 .P
 The
 .BR futex (2)
-system call can optionally be passed a timeout specifying how long
+and
+.BR futex_waitv (2)
+system calls can optionally be passed a timeout specifying how long
 the kernel should
 wait for the futex to be upped.
 In this case, semantics are more complex and the programmer is referred
@@ -107,6 +111,7 @@ .SH NOTES
 .SH SEE ALSO
 .BR clone (2),
 .BR futex (2),
+.BR futex_waitv (2),
 .BR get_robust_list (2),
 .BR set_robust_list (2),
 .BR set_tid_address (2),
-- 
2.39.5

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* Re: [PATCH v7] futex_waitv.2: new page
  2026-02-11 14:44                   ` [PATCH v7] " наб
@ 2026-02-11 14:55                     ` Alejandro Colomar
  2026-02-11 14:59                       ` наб
  2026-02-14 17:32                     ` Alejandro Colomar
  1 sibling, 1 reply; 53+ messages in thread
From: Alejandro Colomar @ 2026-02-11 14:55 UTC (permalink / raw)
  To: наб; +Cc: linux-man

[-- Attachment #1: Type: text/plain, Size: 14215 bytes --]

Hi,

On 2026-02-11T15:44:20+0100, наб wrote:
> Signed-off-by: Ahelenia Ziemiańska <nabijaczleweli@nabijaczleweli.xyz>
> ---
> Range-diff against v5:
> 1:  d221a28a3 ! 1:  da50b4733 futex_waitv.2: new page
[...]
>     @@ man/man2/futex_waitv.2 (new)
>      +#include <unistd.h>
>      +\&
>      +static inline long
>     -+my_futex_wait_private(_Atomic uint32_t *uaddr, uint32_t val,
>     -+                      const struct timespec *timeout)
>     ++my_futex_wait_private(_Atomic uint32_t *uaddr, uint32_t val)
>      +{
>     -+	return syscall(SYS_futex, uaddr, FUTEX_WAKE_PRIVATE, val, timeout);
>     ++	return syscall(SYS_futex, uaddr, FUTEX_WAKE_PRIVATE, val);

I don't think it's valid to call futex(2) with FUTEX_WAIT without
a timeout.  Is it?

I think we need to pass NULL explicitly.


Cheers,
Alex

>      +}
>      +\&
>      +static inline long
>     -+my_futex_waitv(struct futex_waitv *waiters, unsigned int n,
>     ++my_futex_waitv(unsigned int n;
>     ++               struct futex_waitv waiters[n], unsigned int n,
>      +               unsigned int flags, const struct timespec *timeout,
>      +               clockid_t clockid)
>      +{
>     @@ man/man2/futex_waitv.2 (new)
>      +\&
>      +	usleep(*futex * 10000);
>      +	*futex *= 2;
>     -+	my_futex_wait_private(futex, 1, NULL);
>     ++	my_futex_wait_private(futex, 1);
>      +	return NULL;
>      +}
>      +\&
> 
>  man/man2/futex_waitv.2 | 421 +++++++++++++++++++++++++++++++++++++++++
>  man/man7/futex.7       |   9 +-
>  2 files changed, 428 insertions(+), 2 deletions(-)
>  create mode 100644 man/man2/futex_waitv.2
> 
> diff --git u/man/man2/futex_waitv.2 p/man/man2/futex_waitv.2
> new file mode 100644
> index 000000000..a1eeb8ce8
> --- /dev/null
> +++ p/man/man2/futex_waitv.2
> @@ -0,0 +1,421 @@
> +.\" Copyright, the authors of the Linux man-pages project
> +.\"
> +.\" SPDX-License-Identifier: MIT
> +.\"
> +.TH futex_waitv 2 (date) "Linux man-pages (unreleased)"
> +.SH NAME
> +futex_waitv \- wait for FUTEX_WAKE operation on multiple 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 pointed to by the addresses
> +.IR waiters []. uaddr
> +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 not NULL,
> +.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.
> +If
> +.I timeout
> +is NULL,
> +the call blocks indefinitely.
> +.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 one of
> +.BI FUTEX2_SIZE_ *
> +ORed with some of the flags below.
> +.TP
> +.B FUTEX2_SIZE_U32
> +.I .val
> +and
> +.I .uaddr[]
> +are 32-bit unsigned integers.
> +.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 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.
> +.\" commit cec199c5e39bde7191a08087cc3d002ccfab31ff
> +.\" Author: Peter Zijlstra <peterz@infradead.org>
> +.\" Date:   Wed Apr 16 18:29:16 2025 +0200
> +.\"
> +.\"     futex: Implement FUTEX2_NUMA
> +.\"
> +.\" FUTEX2_MPOL is not documented or used anywhere;
> +.\" it's unclear to me what it does
> +.\" (defined in commit c042c505210dc3453f378df432c10fff3d471bc5
> +.\"  "futex: Implement FUTEX2_MPOL")
> +.TP
> +.B FUTEX2_PRIVATE
> +By default, the futex is shared
> +.RB "(like " 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
> +.BR 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 ,
> +.I FUTEX_WAITV_MAX
> +(128)].
> +.TP
> +.B EINVAL
> +.I timeout
> +was not NULL and
> +.I clockid
> +was not a valid clock
> +.RB ( CLOCK_MONOTONIC
> +or
> +.BR CLOCK_REALTIME ).
> +.TP
> +.B EINVAL
> +.I *timeout
> +is denormal (before epoch or
> +.I tv_nsec
> +more than 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 is missing a
> +.BI FUTEX2_SIZE_ *
> +flag or has a size flag different than
> +.B FUTEX2_SIZE_U32
> +set.
> +.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 index of the biggest NUMA domain
> +(for example,
> +.B FUTEX2_SIZE_U8
> +and there are more than 255 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
> +.I timeout
> +was not NULL and no futex was woken before the timeout elapsed.
> +.TP
> +.BR EAGAIN " or " EWOULDBLOCK
> +The value pointed to by
> +.I .uaddr
> +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 NOTES
> +.BR FUTEX2_SIZE_U8 ,
> +.BR FUTEX2_SIZE_U16 ,
> +and
> +.B FUTEX2_SIZE_U64
> +where
> +.I .val
> +and
> +.I *.uaddr
> +are 8, 16, or 64 bits are defined, but not implemented
> +.RB ( EINVAL ).
> +.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 ().
> +.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!
> +Connection timed out
> +.EE
> +.P
> +.\" SRC BEGIN (futex_waitv.c)
> +.EX
> +#include <errno.h>
> +#include <inttypes.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_wait_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)] = {};
> +	int  i;
> +\&
> +	getentropy(init, sizeof(init));
> +	init[0] = init[1] = init[2];
> +	for (i = 0; i < countof(futexes); ++i) {
> +		printf("%" PRIu8 "\[rs]t", init[i]);
> +		atomic_init(&futexes[i], init[i]);
> +		pthread_create(&(pthread_t){}, NULL, worker, &futexes[i]);
> +	}
> +	putchar('\[rs]n');
> +\&
> +	for (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 (;;) {
> +		struct timespec  timeout;
> +		int  woke;
> +\&
> +		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))
> +			break;
> +\&
> +		for (i = 0; i < countof(futexes); ++i) {
> +			if (futexes[i] != waiters[i].val)
> +				printf("%" PRIu32 "%s", futexes[i], i == woke ? "!" : "");
> +			putchar('\[rs]t');
> +		}
> +		putchar('\[rs]n');
> +\&
> +		for (i = 0; i < countof(futexes); ++i)
> +			waiters[i].val = futexes[i];
> +	}
> +	fprintf(stderr, "%s\[rs]n", strerror(errno));
> +}
> +.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
> diff --git u/man/man7/futex.7 p/man/man7/futex.7
> index 51c5d5d9b..d271144ff 100644
> --- u/man/man7/futex.7
> +++ p/man/man7/futex.7
> @@ -45,7 +45,9 @@ .SS Semantics
>  Any futex operation starts in user space,
>  but it may be necessary to communicate with the kernel using the
>  .BR futex (2)
> -system call.
> +or
> +.BR futex_waitv (2)
> +system calls.
>  .P
>  To "up" a futex, execute the proper assembler instructions that
>  will cause the host CPU to atomically increment the integer.
> @@ -72,7 +74,9 @@ .SS Semantics
>  .P
>  The
>  .BR futex (2)
> -system call can optionally be passed a timeout specifying how long
> +and
> +.BR futex_waitv (2)
> +system calls can optionally be passed a timeout specifying how long
>  the kernel should
>  wait for the futex to be upped.
>  In this case, semantics are more complex and the programmer is referred
> @@ -107,6 +111,7 @@ .SH NOTES
>  .SH SEE ALSO
>  .BR clone (2),
>  .BR futex (2),
> +.BR futex_waitv (2),
>  .BR get_robust_list (2),
>  .BR set_robust_list (2),
>  .BR set_tid_address (2),
> -- 
> 2.39.5



-- 
<https://www.alejandro-colomar.es>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v7] futex_waitv.2: new page
  2026-02-11 14:55                     ` Alejandro Colomar
@ 2026-02-11 14:59                       ` наб
  2026-02-11 15:13                         ` Alejandro Colomar
  0 siblings, 1 reply; 53+ messages in thread
From: наб @ 2026-02-11 14:59 UTC (permalink / raw)
  To: Alejandro Colomar; +Cc: linux-man

[-- Attachment #1: Type: text/plain, Size: 583 bytes --]

On Wed, Feb 11, 2026 at 03:55:54PM +0100, Alejandro Colomar wrote:
> On 2026-02-11T15:44:20+0100, наб wrote:
> >     -+	return syscall(SYS_futex, uaddr, FUTEX_WAKE_PRIVATE, val, timeout);
> >     ++	return syscall(SYS_futex, uaddr, FUTEX_WAKE_PRIVATE, val);
> I don't think it's valid to call futex(2) with FUTEX_WAIT without
> a timeout.  Is it?
Correct, it is not. However,

> I think we need to pass NULL explicitly.
if you pay particular attention to the second pair of letters in the
futex operation, you will no doubt come to the conclusion that we don't.

>_<

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v7] futex_waitv.2: new page
  2026-02-11 14:59                       ` наб
@ 2026-02-11 15:13                         ` Alejandro Colomar
  0 siblings, 0 replies; 53+ messages in thread
From: Alejandro Colomar @ 2026-02-11 15:13 UTC (permalink / raw)
  To: наб; +Cc: linux-man

[-- Attachment #1: Type: text/plain, Size: 825 bytes --]

On 2026-02-11T15:59:41+0100, наб wrote:
> On Wed, Feb 11, 2026 at 03:55:54PM +0100, Alejandro Colomar wrote:
> > On 2026-02-11T15:44:20+0100, наб wrote:
> > >     -+	return syscall(SYS_futex, uaddr, FUTEX_WAKE_PRIVATE, val, timeout);
> > >     ++	return syscall(SYS_futex, uaddr, FUTEX_WAKE_PRIVATE, val);
> > I don't think it's valid to call futex(2) with FUTEX_WAIT without
> > a timeout.  Is it?
> Correct, it is not. However,
> 
> > I think we need to pass NULL explicitly.
> if you pay particular attention to the second pair of letters in the
> futex operation, you will no doubt come to the conclusion that we don't.

Ohhh, I had the same problem when splitting the futex(2) page.
I confused those two rather often.  :D

Cheers,
Alex

> 
> >_<



-- 
<https://www.alejandro-colomar.es>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v2] futex_waitv.2: new page
  2026-02-11  7:35           ` Jeff King
  2026-02-11  8:15             ` Kristoffer Haugsbakk
@ 2026-02-11 15:43             ` Junio C Hamano
  1 sibling, 0 replies; 53+ messages in thread
From: Junio C Hamano @ 2026-02-11 15:43 UTC (permalink / raw)
  To: Jeff King
  Cc: Kristoffer Haugsbakk, Alejandro Colomar, git,
	наб, linux-man

Jeff King <peff@peff.net> writes:

> On Tue, Feb 10, 2026 at 04:54:02PM +0100, Kristoffer Haugsbakk wrote:
>
>> An aside but `format.noprefix` is not a boolean like `diff.noprefix`.
>> It will be enabled with any value.
>
> Huh, that's weird. Indeed, the code is:
>
>           if (!strcmp(var, "format.noprefix")) {
>                   format_no_prefix = 1;
>                   return 0;
>           }
>
> which seems just wrong. I cannot think of any other case where the
> _existence_ of a config variable determines the outcome, rather than the
> assigned value. So I would be inclined to call this a bug and switch it
> to use git_config_bool().

Very true.  It is surprising that nobody noticed it during the review.


> Technically that is not backwards-compatible, but I really think the
> existing behavior is just a bug. It was not something intended and is
> contrary to how the rest of Git works. Presumably nobody noticed because
> why in the world would you set it to false in the first place?
>
>> Is it standard to indicate this with the existing “If set,”, perhaps? Or
>> should it say “enabled with any value”?
>> 
>> (+Cc Peff)
>> 
>>     format.noprefix::
>>             If set, do not show any source or destination prefix in patches.
>>             This is equivalent to the `diff.noprefix` option used by `git
>>             diff` (but which is not respected by `format-patch`). Note that
>>             by setting this, the receiver of any patches you generate will
>>             have to apply them using the `-p0` option.
>
> We usually use "if set" in the config documentation to refer to options
> being set to the true value. So I think the documentation text is OK and
> would match the code, once fixed.
>
> Kristoffer, do you want to produce a patch to fix the code? I feel like
> finding it was 99% of the work. ;)
>
> -Peff

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v7] futex_waitv.2: new page
  2026-02-11 14:44                   ` [PATCH v7] " наб
  2026-02-11 14:55                     ` Alejandro Colomar
@ 2026-02-14 17:32                     ` Alejandro Colomar
  2026-02-14 19:30                       ` [PATCH v8] " наб
  1 sibling, 1 reply; 53+ messages in thread
From: Alejandro Colomar @ 2026-02-14 17:32 UTC (permalink / raw)
  To: наб; +Cc: linux-man

[-- Attachment #1: Type: text/plain, Size: 7419 bytes --]

Hi наб,

On 2026-02-11T15:44:20+0100, наб wrote:
> Signed-off-by: Ahelenia Ziemiańska <nabijaczleweli@nabijaczleweli.xyz>
> ---
> Range-diff against v5:
[...]
Thanks!

> 
>  man/man2/futex_waitv.2 | 421 +++++++++++++++++++++++++++++++++++++++++
>  man/man7/futex.7       |   9 +-
>  2 files changed, 428 insertions(+), 2 deletions(-)
>  create mode 100644 man/man2/futex_waitv.2
> 
> diff --git u/man/man2/futex_waitv.2 p/man/man2/futex_waitv.2
> new file mode 100644
> index 000000000..a1eeb8ce8
> --- /dev/null
> +++ p/man/man2/futex_waitv.2
> @@ -0,0 +1,421 @@
[...]
> +.SH SYNOPSIS
> +.nf
> +.BR "#include <linux/futex.h>" "  /* Definition of " FUTEX* " constants */"

Out of curiosity, why are some macros FUTEX2_* instead of FUTEX_*?
(if you know)

> +.BR "#include <sys/syscall.h>" "  /* Definition of " SYS_* " constants */"
> +.B #include <unistd.h>
> +.B #include <time.h>
> +.P
> +.SH ERRORS
[...]
> +.TP
> +.B EINVAL
> +.I n
> +was not in the range
> +.RB [ 1 ,
> +.I FUTEX_WAITV_MAX
> +(128)].

Ahhh, so 128 is the value of FUTEX_WAITV_MAX, not its argument.
I prefer not showing that value here.  Let's just say

	.IR FUTEX_WAITV_MAX ].

In general, we don't hard-code magic values unless users *need* to know
them.  This makes it more evident that if users rely on a specific
value, that may carry portability problems.

[...]
> +.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 index of the biggest NUMA domain
> +(for example,
> +.B FUTEX2_SIZE_U8
> +and there are more than 255 NUMA domains).

Is it 255 or 256?  I assume it's a 0-based index, so I'd expect there to
fit 256 indices in a u8.

[...]
> +.SH NOTES
> +.BR FUTEX2_SIZE_U8 ,
> +.BR FUTEX2_SIZE_U16 ,
> +and
> +.B FUTEX2_SIZE_U64
> +where
> +.I .val
> +and
> +.I *.uaddr

This should also be .uaddr[]

> +are 8, 16, or 64 bits are defined, but not implemented
> +.RB ( EINVAL ).
[...]
> +.SH EXAMPLES
[...]
> +.EX
> +.RB $\~ ./futex_waitv
> +153	153	153	237	100	245	177	127	215	61
> +									122!
> +				200!
> +							254!
> +306	306!
> +		306!
> +						354!
> +								430!
> +			474!
> +					490!
> +Connection timed out
> +.EE
> +.P
> +.\" SRC BEGIN (futex_waitv.c)
> +.EX
> +#include <errno.h>
> +#include <inttypes.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_wait_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)] = {};
> +	int  i;
> +\&
> +	getentropy(init, sizeof(init));

$ make -R lint build-all check CC=/opt/local/gnu/gcc/cap4/bin/gcc 
...
.tmp/man/man2/futex_waitv.2.d/futex_waitv.c:50:6: error: ignoring return value of ‘getentropy’ declared with attribute ‘warn_unused_result’ [-Werror=unused-result]
   50 |      getentropy(init, sizeof(init));
      |      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
At top level:
cc1: note: unrecognized command-line option ‘-Wno-reserved-identifier’ may have been intended to silence earlier diagnostics
cc1: all warnings being treated as errors
make: *** [/srv/alx/src/linux/man-pages/man-pages/contrib/share/mk/build/examples/cc.mk:30: .tmp/man/man2/futex_waitv.2.d/futex_waitv.o] Error 1

> +	init[0] = init[1] = init[2];
> +	for (i = 0; i < countof(futexes); ++i) {
> +		printf("%" PRIu8 "\[rs]t", init[i]);
> +		atomic_init(&futexes[i], init[i]);
> +		pthread_create(&(pthread_t){}, NULL, worker, &futexes[i]);
> +	}
> +	putchar('\[rs]n');

Here and elsewhere in the example: \[aq] instea of '

> +\&
> +	for (i = 0; i < countof(futexes); ++i) {
> +		waiters[i].val   = futexes[i];
> +		waiters[i].uaddr = (uintptr_t)&futexes[i];

Please add a space between casts and the expression their operand.

> +		waiters[i].flags = FUTEX2_SIZE_U32 | FUTEX2_PRIVATE;
> +	}
> +	for (;;) {
> +		struct timespec  timeout;
> +		int  woke;
> +\&
> +		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))

$ make -R lint-man-dash
make: warning: undefined variable 'GNUMAKEFLAGS'
PCRE2GREP	.tmp/man/man2/futex_waitv.2.lint-man.dash.touch
lint-man-dash: .tmp/man/man2/futex_waitv.2: Unescaped dash:
    397:			if (woke == -1 && (errno != EAGAIN && errno != EWOULDBLOCK))
make: *** [/srv/alx/src/linux/man-pages/man-pages/contrib/share/mk/lint/man/dash.mk:26: .tmp/man/man2/futex_waitv.2.lint-man.dash.touch] Error 1


Have a lovely day!
Alex

> +			break;
> +\&
> +		for (i = 0; i < countof(futexes); ++i) {
> +			if (futexes[i] != waiters[i].val)
> +				printf("%" PRIu32 "%s", futexes[i], i == woke ? "!" : "");
> +			putchar('\[rs]t');
> +		}
> +		putchar('\[rs]n');
> +\&
> +		for (i = 0; i < countof(futexes); ++i)
> +			waiters[i].val = futexes[i];
> +	}
> +	fprintf(stderr, "%s\[rs]n", strerror(errno));
> +}
> +.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
> diff --git u/man/man7/futex.7 p/man/man7/futex.7
> index 51c5d5d9b..d271144ff 100644
> --- u/man/man7/futex.7
> +++ p/man/man7/futex.7
> @@ -45,7 +45,9 @@ .SS Semantics
>  Any futex operation starts in user space,
>  but it may be necessary to communicate with the kernel using the
>  .BR futex (2)
> -system call.
> +or
> +.BR futex_waitv (2)
> +system calls.
>  .P
>  To "up" a futex, execute the proper assembler instructions that
>  will cause the host CPU to atomically increment the integer.
> @@ -72,7 +74,9 @@ .SS Semantics
>  .P
>  The
>  .BR futex (2)
> -system call can optionally be passed a timeout specifying how long
> +and
> +.BR futex_waitv (2)
> +system calls can optionally be passed a timeout specifying how long
>  the kernel should
>  wait for the futex to be upped.
>  In this case, semantics are more complex and the programmer is referred
> @@ -107,6 +111,7 @@ .SH NOTES
>  .SH SEE ALSO
>  .BR clone (2),
>  .BR futex (2),
> +.BR futex_waitv (2),
>  .BR get_robust_list (2),
>  .BR set_robust_list (2),
>  .BR set_tid_address (2),
> -- 
> 2.39.5



-- 
<https://www.alejandro-colomar.es>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [PATCH v8] futex_waitv.2: new page
  2026-02-14 17:32                     ` Alejandro Colomar
@ 2026-02-14 19:30                       ` наб
  2026-02-14 20:03                         ` Alejandro Colomar
  0 siblings, 1 reply; 53+ messages in thread
From: наб @ 2026-02-14 19:30 UTC (permalink / raw)
  To: Alejandro Colomar; +Cc: linux-man

[-- Attachment #1: Type: text/plain, Size: 16335 bytes --]

On Sat, Feb 14, 2026 at 06:32:17PM +0100, Alejandro Colomar wrote:
> > diff --git u/man/man2/futex_waitv.2 p/man/man2/futex_waitv.2
> > new file mode 100644
> > index 000000000..a1eeb8ce8
> > --- /dev/null
> > +++ p/man/man2/futex_waitv.2
> > @@ -0,0 +1,421 @@
> [...]
> > +.SH SYNOPSIS
> > +.nf
> > +.BR "#include <linux/futex.h>" "  /* Definition of " FUTEX* " constants */"
> Out of curiosity, why are some macros FUTEX2_* instead of FUTEX_*?
> (if you know)
They call the futex_*() syscalls "futex2", in contrast
to the futex(FUTEX_*) family which is (retronymically) version 1 futex;
futex2-specific macros start with FUTEX2_, the original futex macros
started with just FUTEX_.

> > +.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 index of the biggest NUMA domain
> > +(for example,
> > +.B FUTEX2_SIZE_U8
> > +and there are more than 255 NUMA domains).
> Is it 255 or 256?  I assume it's a 0-based index, so I'd expect there to
> fit 256 indices in a u8.
kernel/futex/futex.h:
	int bits = 8 * futex_size(flags);  // 8
	u64 max = ~0ULL;                   // 0xFFFF`FFFF`FFFF`FFFF
	
	max >>= 64 - bits;                 // 0xFF
	if (nr_node_ids >= max)
		return false;
which is first true when nr_node_ids is 0xFF,
so "FUTEX2_SIZE_U8 and at least 255", actually.

Also this variable is "/possible/ NUMA domains" apparently.

Scissor-patch below.

Best,
-- >8 --
From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= <nabijaczleweli@nabijaczleweli.xyz>
Date: Tue, 10 Feb 2026 21:32:19 +0100
Subject: [PATCH v8] futex_waitv.2: new page

Signed-off-by: Ahelenia Ziemiańska <nabijaczleweli@nabijaczleweli.xyz>
---
Range-diff against v7:
1:  da50b4733 ! 1:  c63cce1ec futex_waitv.2: new page
    @@ man/man2/futex_waitv.2 (new)
     +.I n
     +was not in the range
     +.RB [ 1 ,
    -+.I FUTEX_WAITV_MAX
    -+(128)].
    ++.IR FUTEX_WAITV_MAX ].
     +.TP
     +.B EINVAL
     +.I timeout
    @@ man/man2/futex_waitv.2 (new)
     +is too small to contain the index of the biggest NUMA domain
     +(for example,
     +.B FUTEX2_SIZE_U8
    -+and there are more than 255 NUMA domains).
    ++and there are at least 255 possible NUMA domains).
     +.TP
     +.B EINVAL
     +.B FUTEX2_NUMA
    @@ man/man2/futex_waitv.2 (new)
     +where
     +.I .val
     +and
    -+.I *.uaddr
    ++.I .uaddr[]
     +are 8, 16, or 64 bits are defined, but not implemented
     +.RB ( EINVAL ).
     +.SH HISTORY
    @@ man/man2/futex_waitv.2 (new)
     +.P
     +.\" SRC BEGIN (futex_waitv.c)
     +.EX
    ++#include <err.h>
     +#include <errno.h>
     +#include <inttypes.h>
     +#include <linux/futex.h>
    @@ man/man2/futex_waitv.2 (new)
     +	struct futex_waitv waiters[countof(futexes)] = {};
     +	int  i;
     +\&
    -+	getentropy(init, sizeof(init));
    ++	if(getentropy(init, sizeof(init)))
    ++		err(EXIT_FAILURE, "getentropy");
     +	init[0] = init[1] = init[2];
     +	for (i = 0; i < countof(futexes); ++i) {
     +		printf("%" PRIu8 "\[rs]t", init[i]);
     +		atomic_init(&futexes[i], init[i]);
    -+		pthread_create(&(pthread_t){}, NULL, worker, &futexes[i]);
    ++		pthread_create(&(pthread_t) {}, NULL, worker, &futexes[i]);
     +	}
    -+	putchar('\[rs]n');
    ++	putchar(\[aq]\[rs]n\[aq]);
     +\&
     +	for (i = 0; i < countof(futexes); ++i) {
     +		waiters[i].val   = futexes[i];
    -+		waiters[i].uaddr = (uintptr_t)&futexes[i];
    ++		waiters[i].uaddr = (uintptr_t) &futexes[i];
     +		waiters[i].flags = FUTEX2_SIZE_U32 | FUTEX2_PRIVATE;
     +	}
     +	for (;;) {
    @@ man/man2/futex_waitv.2 (new)
     +		timeout.tv_sec += 1;
     +\&
     +		woke = my_futex_waitv(waiters, countof(futexes), 0, &timeout, CLOCK_MONOTONIC);
    -+		if (woke == -1 && (errno != EAGAIN && errno != EWOULDBLOCK))
    ++		if (woke == \-1 && (errno != EAGAIN && errno != EWOULDBLOCK))
     +			break;
     +\&
     +		for (i = 0; i < countof(futexes); ++i) {
     +			if (futexes[i] != waiters[i].val)
     +				printf("%" PRIu32 "%s", futexes[i], i == woke ? "!" : "");
    -+			putchar('\[rs]t');
    ++			putchar(\[aq]\[rs]t\[aq]);
     +		}
    -+		putchar('\[rs]n');
    ++		putchar(\[aq]\[rs]n\[aq]);
     +\&
     +		for (i = 0; i < countof(futexes); ++i)
     +			waiters[i].val = futexes[i];

 man/man2/futex_waitv.2 | 422 +++++++++++++++++++++++++++++++++++++++++
 man/man7/futex.7       |   9 +-
 2 files changed, 429 insertions(+), 2 deletions(-)
 create mode 100644 man/man2/futex_waitv.2

diff --git u/man/man2/futex_waitv.2 p/man/man2/futex_waitv.2
new file mode 100644
index 000000000..6835434b4
--- /dev/null
+++ p/man/man2/futex_waitv.2
@@ -0,0 +1,422 @@
+.\" Copyright, the authors of the Linux man-pages project
+.\"
+.\" SPDX-License-Identifier: MIT
+.\"
+.TH futex_waitv 2 (date) "Linux man-pages (unreleased)"
+.SH NAME
+futex_waitv \- wait for FUTEX_WAKE operation on multiple 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 pointed to by the addresses
+.IR waiters []. uaddr
+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 not NULL,
+.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.
+If
+.I timeout
+is NULL,
+the call blocks indefinitely.
+.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 one of
+.BI FUTEX2_SIZE_ *
+ORed with some of the flags below.
+.TP
+.B FUTEX2_SIZE_U32
+.I .val
+and
+.I .uaddr[]
+are 32-bit unsigned integers.
+.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 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.
+.\" commit cec199c5e39bde7191a08087cc3d002ccfab31ff
+.\" Author: Peter Zijlstra <peterz@infradead.org>
+.\" Date:   Wed Apr 16 18:29:16 2025 +0200
+.\"
+.\"     futex: Implement FUTEX2_NUMA
+.\"
+.\" FUTEX2_MPOL is not documented or used anywhere;
+.\" it's unclear to me what it does
+.\" (defined in commit c042c505210dc3453f378df432c10fff3d471bc5
+.\"  "futex: Implement FUTEX2_MPOL")
+.TP
+.B FUTEX2_PRIVATE
+By default, the futex is shared
+.RB "(like " 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
+.BR 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 ,
+.IR FUTEX_WAITV_MAX ].
+.TP
+.B EINVAL
+.I timeout
+was not NULL and
+.I clockid
+was not a valid clock
+.RB ( CLOCK_MONOTONIC
+or
+.BR CLOCK_REALTIME ).
+.TP
+.B EINVAL
+.I *timeout
+is denormal (before epoch or
+.I tv_nsec
+more than 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 is missing a
+.BI FUTEX2_SIZE_ *
+flag or has a size flag different than
+.B FUTEX2_SIZE_U32
+set.
+.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 index of the biggest 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
+.I timeout
+was not NULL and no futex was woken before the timeout elapsed.
+.TP
+.BR EAGAIN " or " EWOULDBLOCK
+The value pointed to by
+.I .uaddr
+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 NOTES
+.BR FUTEX2_SIZE_U8 ,
+.BR FUTEX2_SIZE_U16 ,
+and
+.B FUTEX2_SIZE_U64
+where
+.I .val
+and
+.I .uaddr[]
+are 8, 16, or 64 bits are defined, but not implemented
+.RB ( EINVAL ).
+.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 ().
+.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!
+Connection timed out
+.EE
+.P
+.\" SRC BEGIN (futex_waitv.c)
+.EX
+#include <err.h>
+#include <errno.h>
+#include <inttypes.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_wait_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)] = {};
+	int  i;
+\&
+	if(getentropy(init, sizeof(init)))
+		err(EXIT_FAILURE, "getentropy");
+	init[0] = init[1] = init[2];
+	for (i = 0; i < countof(futexes); ++i) {
+		printf("%" PRIu8 "\[rs]t", init[i]);
+		atomic_init(&futexes[i], init[i]);
+		pthread_create(&(pthread_t) {}, NULL, worker, &futexes[i]);
+	}
+	putchar(\[aq]\[rs]n\[aq]);
+\&
+	for (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 (;;) {
+		struct timespec  timeout;
+		int  woke;
+\&
+		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))
+			break;
+\&
+		for (i = 0; i < countof(futexes); ++i) {
+			if (futexes[i] != waiters[i].val)
+				printf("%" PRIu32 "%s", futexes[i], i == woke ? "!" : "");
+			putchar(\[aq]\[rs]t\[aq]);
+		}
+		putchar(\[aq]\[rs]n\[aq]);
+\&
+		for (i = 0; i < countof(futexes); ++i)
+			waiters[i].val = futexes[i];
+	}
+	fprintf(stderr, "%s\[rs]n", strerror(errno));
+}
+.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
diff --git u/man/man7/futex.7 p/man/man7/futex.7
index 51c5d5d9b..d271144ff 100644
--- u/man/man7/futex.7
+++ p/man/man7/futex.7
@@ -45,7 +45,9 @@ .SS Semantics
 Any futex operation starts in user space,
 but it may be necessary to communicate with the kernel using the
 .BR futex (2)
-system call.
+or
+.BR futex_waitv (2)
+system calls.
 .P
 To "up" a futex, execute the proper assembler instructions that
 will cause the host CPU to atomically increment the integer.
@@ -72,7 +74,9 @@ .SS Semantics
 .P
 The
 .BR futex (2)
-system call can optionally be passed a timeout specifying how long
+and
+.BR futex_waitv (2)
+system calls can optionally be passed a timeout specifying how long
 the kernel should
 wait for the futex to be upped.
 In this case, semantics are more complex and the programmer is referred
@@ -107,6 +111,7 @@ .SH NOTES
 .SH SEE ALSO
 .BR clone (2),
 .BR futex (2),
+.BR futex_waitv (2),
 .BR get_robust_list (2),
 .BR set_robust_list (2),
 .BR set_tid_address (2),
-- 
2.39.5


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* Re: [PATCH v8] futex_waitv.2: new page
  2026-02-14 19:30                       ` [PATCH v8] " наб
@ 2026-02-14 20:03                         ` Alejandro Colomar
  2026-02-14 20:48                           ` [PATCH v9] " наб
  0 siblings, 1 reply; 53+ messages in thread
From: Alejandro Colomar @ 2026-02-14 20:03 UTC (permalink / raw)
  To: наб; +Cc: linux-man

[-- Attachment #1: Type: text/plain, Size: 6668 bytes --]

Hi,

On 2026-02-14T20:30:37+0100, наб wrote:
> On Sat, Feb 14, 2026 at 06:32:17PM +0100, Alejandro Colomar wrote:
> > > diff --git u/man/man2/futex_waitv.2 p/man/man2/futex_waitv.2
> > > new file mode 100644
> > > index 000000000..a1eeb8ce8
> > > --- /dev/null
> > > +++ p/man/man2/futex_waitv.2
> > > @@ -0,0 +1,421 @@
> > [...]
> > > +.SH SYNOPSIS
> > > +.nf
> > > +.BR "#include <linux/futex.h>" "  /* Definition of " FUTEX* " constants */"
> > Out of curiosity, why are some macros FUTEX2_* instead of FUTEX_*?
> > (if you know)
> They call the futex_*() syscalls "futex2", in contrast
> to the futex(FUTEX_*) family which is (retronymically) version 1 futex;
> futex2-specific macros start with FUTEX2_, the original futex macros
> started with just FUTEX_.

Thanks!

> 
> > > +.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 index of the biggest NUMA domain
> > > +(for example,
> > > +.B FUTEX2_SIZE_U8
> > > +and there are more than 255 NUMA domains).
> > Is it 255 or 256?  I assume it's a 0-based index, so I'd expect there to
> > fit 256 indices in a u8.
> kernel/futex/futex.h:
> 	int bits = 8 * futex_size(flags);  // 8
> 	u64 max = ~0ULL;                   // 0xFFFF`FFFF`FFFF`FFFF
> 	
> 	max >>= 64 - bits;                 // 0xFF
> 	if (nr_node_ids >= max)
> 		return false;
> which is first true when nr_node_ids is 0xFF,
> so "FUTEX2_SIZE_U8 and at least 255", actually.
> 
> Also this variable is "/possible/ NUMA domains" apparently.

Hmmm.

> 
> Scissor-patch below.
> 
> Best,
> -- >8 --
> From: =?UTF-8?q?=D0=BD=D0=B0=D0=B1?= <nabijaczleweli@nabijaczleweli.xyz>
> Date: Tue, 10 Feb 2026 21:32:19 +0100
> Subject: [PATCH v8] futex_waitv.2: new page
> 
> Signed-off-by: Ahelenia Ziemiańska <nabijaczleweli@nabijaczleweli.xyz>
> ---
> Range-diff against v7:
> 1:  da50b4733 ! 1:  c63cce1ec futex_waitv.2: new page
[...]
>     @@ man/man2/futex_waitv.2 (new)
>      +.P
>      +.\" SRC BEGIN (futex_waitv.c)
>      +.EX
>     ++#include <err.h>
>      +#include <errno.h>
>      +#include <inttypes.h>
>      +#include <linux/futex.h>
>     @@ man/man2/futex_waitv.2 (new)
>      +	struct futex_waitv waiters[countof(futexes)] = {};
>      +	int  i;
>      +\&
>     -+	getentropy(init, sizeof(init));
>     ++	if(getentropy(init, sizeof(init)))

Space after structural keywords.

>     ++		err(EXIT_FAILURE, "getentropy");
>      +	init[0] = init[1] = init[2];
>      +	for (i = 0; i < countof(futexes); ++i) {
>      +		printf("%" PRIu8 "\[rs]t", init[i]);
>      +		atomic_init(&futexes[i], init[i]);
>     -+		pthread_create(&(pthread_t){}, NULL, worker, &futexes[i]);
>     ++		pthread_create(&(pthread_t) {}, NULL, worker, &futexes[i]);

This is not a cast, so it should not have a space.

>      +	}
>     -+	putchar('\[rs]n');
>     ++	putchar(\[aq]\[rs]n\[aq]);
>      +\&
>      +	for (i = 0; i < countof(futexes); ++i) {
>      +		waiters[i].val   = futexes[i];
>     -+		waiters[i].uaddr = (uintptr_t)&futexes[i];
>     ++		waiters[i].uaddr = (uintptr_t) &futexes[i];
>      +		waiters[i].flags = FUTEX2_SIZE_U32 | FUTEX2_PRIVATE;
>      +	}
>      +	for (;;) {
[...]
> 
>  man/man2/futex_waitv.2 | 422 +++++++++++++++++++++++++++++++++++++++++
>  man/man7/futex.7       |   9 +-
>  2 files changed, 429 insertions(+), 2 deletions(-)
>  create mode 100644 man/man2/futex_waitv.2
> 
> diff --git u/man/man2/futex_waitv.2 p/man/man2/futex_waitv.2
> new file mode 100644
> index 000000000..6835434b4
> --- /dev/null
> +++ p/man/man2/futex_waitv.2
[...]
> +.SH EXAMPLES
[...]
> +int
> +main(void)
> +{
[...]
> +	if(getentropy(init, sizeof(init)))
> +		err(EXIT_FAILURE, "getentropy");
> +	init[0] = init[1] = init[2];
> +	for (i = 0; i < countof(futexes); ++i) {
> +		printf("%" PRIu8 "\[rs]t", init[i]);
> +		atomic_init(&futexes[i], init[i]);
> +		pthread_create(&(pthread_t) {}, NULL, worker, &futexes[i]);

Not a cast.

> +	}
> +	putchar(\[aq]\[rs]n\[aq]);
> +\&
> +	for (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 (;;) {
> +		struct timespec  timeout;
> +		int  woke;
> +\&
> +		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))
> +			break;
> +\&
> +		for (i = 0; i < countof(futexes); ++i) {
> +			if (futexes[i] != waiters[i].val)
> +				printf("%" PRIu32 "%s", futexes[i], i == woke ? "!" : "");
> +			putchar(\[aq]\[rs]t\[aq]);
> +		}
> +		putchar(\[aq]\[rs]n\[aq]);
> +\&
> +		for (i = 0; i < countof(futexes); ++i)
> +			waiters[i].val = futexes[i];
> +	}
> +	fprintf(stderr, "%s\[rs]n", strerror(errno));

This looks like 'perror("")', doesn't it?

Although, I'd use err(3) instead, which correctly fails instead of
returning 0, right?

	err(EXIT_FAILURE, "my_futex_waitv");


Have a lovely night!
Alex

> +}
> +.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
> diff --git u/man/man7/futex.7 p/man/man7/futex.7
> index 51c5d5d9b..d271144ff 100644
> --- u/man/man7/futex.7
> +++ p/man/man7/futex.7
> @@ -45,7 +45,9 @@ .SS Semantics
>  Any futex operation starts in user space,
>  but it may be necessary to communicate with the kernel using the
>  .BR futex (2)
> -system call.
> +or
> +.BR futex_waitv (2)
> +system calls.
>  .P
>  To "up" a futex, execute the proper assembler instructions that
>  will cause the host CPU to atomically increment the integer.
> @@ -72,7 +74,9 @@ .SS Semantics
>  .P
>  The
>  .BR futex (2)
> -system call can optionally be passed a timeout specifying how long
> +and
> +.BR futex_waitv (2)
> +system calls can optionally be passed a timeout specifying how long
>  the kernel should
>  wait for the futex to be upped.
>  In this case, semantics are more complex and the programmer is referred
> @@ -107,6 +111,7 @@ .SH NOTES
>  .SH SEE ALSO
>  .BR clone (2),
>  .BR futex (2),
> +.BR futex_waitv (2),
>  .BR get_robust_list (2),
>  .BR set_robust_list (2),
>  .BR set_tid_address (2),
> -- 
> 2.39.5
> 



-- 
<https://www.alejandro-colomar.es>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [PATCH v9] futex_waitv.2: new page
  2026-02-14 20:03                         ` Alejandro Colomar
@ 2026-02-14 20:48                           ` наб
  2026-02-15 18:18                             ` Alejandro Colomar
  0 siblings, 1 reply; 53+ messages in thread
From: наб @ 2026-02-14 20:48 UTC (permalink / raw)
  To: Alejandro Colomar; +Cc: linux-man

[-- Attachment #1: Type: text/plain, Size: 13486 bytes --]

Signed-off-by: Ahelenia Ziemiańska <nabijaczleweli@nabijaczleweli.xyz>
---
Range-diff against v8:
1:  c63cce1ec ! 1:  76632607a futex_waitv.2: new page
    @@ man/man2/futex_waitv.2 (new)
     +								430!
     +			474!
     +					490!
    -+Connection timed out
    ++futex_waitv: my_futex_waitv: Connection timed out
     +.EE
     +.P
     +.\" SRC BEGIN (futex_waitv.c)
    @@ man/man2/futex_waitv.2 (new)
     +	struct futex_waitv waiters[countof(futexes)] = {};
     +	int  i;
     +\&
    -+	if(getentropy(init, sizeof(init)))
    ++	if (getentropy(init, sizeof(init)))
     +		err(EXIT_FAILURE, "getentropy");
     +	init[0] = init[1] = init[2];
     +	for (i = 0; i < countof(futexes); ++i) {
     +		printf("%" PRIu8 "\[rs]t", init[i]);
     +		atomic_init(&futexes[i], init[i]);
    -+		pthread_create(&(pthread_t) {}, NULL, worker, &futexes[i]);
    ++		pthread_create(&(pthread_t){}, NULL, worker, &futexes[i]);
     +	}
     +	putchar(\[aq]\[rs]n\[aq]);
     +\&
    @@ man/man2/futex_waitv.2 (new)
     +\&
     +		woke = my_futex_waitv(waiters, countof(futexes), 0, &timeout, CLOCK_MONOTONIC);
     +		if (woke == \-1 && (errno != EAGAIN && errno != EWOULDBLOCK))
    -+			break;
    ++			err(EXIT_FAILURE, "my_futex_waitv");
     +\&
     +		for (i = 0; i < countof(futexes); ++i) {
     +			if (futexes[i] != waiters[i].val)
    @@ man/man2/futex_waitv.2 (new)
     +		for (i = 0; i < countof(futexes); ++i)
     +			waiters[i].val = futexes[i];
     +	}
    -+	fprintf(stderr, "%s\[rs]n", strerror(errno));
     +}
     +.EE
     +.\" SRC END

 man/man2/futex_waitv.2 | 421 +++++++++++++++++++++++++++++++++++++++++
 man/man7/futex.7       |   9 +-
 2 files changed, 428 insertions(+), 2 deletions(-)
 create mode 100644 man/man2/futex_waitv.2

diff --git u/man/man2/futex_waitv.2 p/man/man2/futex_waitv.2
new file mode 100644
index 000000000..7437cbfc5
--- /dev/null
+++ p/man/man2/futex_waitv.2
@@ -0,0 +1,421 @@
+.\" Copyright, the authors of the Linux man-pages project
+.\"
+.\" SPDX-License-Identifier: MIT
+.\"
+.TH futex_waitv 2 (date) "Linux man-pages (unreleased)"
+.SH NAME
+futex_waitv \- wait for FUTEX_WAKE operation on multiple 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 pointed to by the addresses
+.IR waiters []. uaddr
+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 not NULL,
+.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.
+If
+.I timeout
+is NULL,
+the call blocks indefinitely.
+.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 one of
+.BI FUTEX2_SIZE_ *
+ORed with some of the flags below.
+.TP
+.B FUTEX2_SIZE_U32
+.I .val
+and
+.I .uaddr[]
+are 32-bit unsigned integers.
+.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 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.
+.\" commit cec199c5e39bde7191a08087cc3d002ccfab31ff
+.\" Author: Peter Zijlstra <peterz@infradead.org>
+.\" Date:   Wed Apr 16 18:29:16 2025 +0200
+.\"
+.\"     futex: Implement FUTEX2_NUMA
+.\"
+.\" FUTEX2_MPOL is not documented or used anywhere;
+.\" it's unclear to me what it does
+.\" (defined in commit c042c505210dc3453f378df432c10fff3d471bc5
+.\"  "futex: Implement FUTEX2_MPOL")
+.TP
+.B FUTEX2_PRIVATE
+By default, the futex is shared
+.RB "(like " 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
+.BR 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 ,
+.IR FUTEX_WAITV_MAX ].
+.TP
+.B EINVAL
+.I timeout
+was not NULL and
+.I clockid
+was not a valid clock
+.RB ( CLOCK_MONOTONIC
+or
+.BR CLOCK_REALTIME ).
+.TP
+.B EINVAL
+.I *timeout
+is denormal (before epoch or
+.I tv_nsec
+more than 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 is missing a
+.BI FUTEX2_SIZE_ *
+flag or has a size flag different than
+.B FUTEX2_SIZE_U32
+set.
+.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 index of the biggest 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
+.I timeout
+was not NULL and no futex was woken before the timeout elapsed.
+.TP
+.BR EAGAIN " or " EWOULDBLOCK
+The value pointed to by
+.I .uaddr
+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 NOTES
+.BR FUTEX2_SIZE_U8 ,
+.BR FUTEX2_SIZE_U16 ,
+and
+.B FUTEX2_SIZE_U64
+where
+.I .val
+and
+.I .uaddr[]
+are 8, 16, or 64 bits are defined, but not implemented
+.RB ( EINVAL ).
+.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 ().
+.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 <inttypes.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_wait_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)] = {};
+	int  i;
+\&
+	if (getentropy(init, sizeof(init)))
+		err(EXIT_FAILURE, "getentropy");
+	init[0] = init[1] = init[2];
+	for (i = 0; i < countof(futexes); ++i) {
+		printf("%" PRIu8 "\[rs]t", init[i]);
+		atomic_init(&futexes[i], init[i]);
+		pthread_create(&(pthread_t){}, NULL, worker, &futexes[i]);
+	}
+	putchar(\[aq]\[rs]n\[aq]);
+\&
+	for (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 (;;) {
+		struct timespec  timeout;
+		int  woke;
+\&
+		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 (i = 0; i < countof(futexes); ++i) {
+			if (futexes[i] != waiters[i].val)
+				printf("%" PRIu32 "%s", futexes[i], i == woke ? "!" : "");
+			putchar(\[aq]\[rs]t\[aq]);
+		}
+		putchar(\[aq]\[rs]n\[aq]);
+\&
+		for (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
diff --git u/man/man7/futex.7 p/man/man7/futex.7
index 51c5d5d9b..d271144ff 100644
--- u/man/man7/futex.7
+++ p/man/man7/futex.7
@@ -45,7 +45,9 @@ .SS Semantics
 Any futex operation starts in user space,
 but it may be necessary to communicate with the kernel using the
 .BR futex (2)
-system call.
+or
+.BR futex_waitv (2)
+system calls.
 .P
 To "up" a futex, execute the proper assembler instructions that
 will cause the host CPU to atomically increment the integer.
@@ -72,7 +74,9 @@ .SS Semantics
 .P
 The
 .BR futex (2)
-system call can optionally be passed a timeout specifying how long
+and
+.BR futex_waitv (2)
+system calls can optionally be passed a timeout specifying how long
 the kernel should
 wait for the futex to be upped.
 In this case, semantics are more complex and the programmer is referred
@@ -107,6 +111,7 @@ .SH NOTES
 .SH SEE ALSO
 .BR clone (2),
 .BR futex (2),
+.BR futex_waitv (2),
 .BR get_robust_list (2),
 .BR set_robust_list (2),
 .BR set_tid_address (2),
-- 
2.39.5

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* Re: [PATCH v9] futex_waitv.2: new page
  2026-02-14 20:48                           ` [PATCH v9] " наб
@ 2026-02-15 18:18                             ` Alejandro Colomar
  2026-02-15 19:00                               ` [PATCH v10] " наб
  0 siblings, 1 reply; 53+ messages in thread
From: Alejandro Colomar @ 2026-02-15 18:18 UTC (permalink / raw)
  To: наб; +Cc: linux-man

[-- Attachment #1: Type: text/plain, Size: 2837 bytes --]

Hi наб,

On 2026-02-14T21:48:01+0100, наб wrote:
[...]
> +static inline long
> +my_futex_wait_private(_Atomic uint32_t *uaddr, uint32_t val)

I just noticed this should be my_futex_wa{it => ke}_private().
Probably caused by my earlier confusion while reading these; sorry!

> +{
> +	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)] = {};
> +	int  i;
> +\&
> +	if (getentropy(init, sizeof(init)))
> +		err(EXIT_FAILURE, "getentropy");
> +	init[0] = init[1] = init[2];
> +	for (i = 0; i < countof(futexes); ++i) {
> +		printf("%" PRIu8 "\[rs]t", init[i]);
> +		atomic_init(&futexes[i], init[i]);
> +		pthread_create(&(pthread_t){}, NULL, worker, &futexes[i]);
> +	}
> +	putchar(\[aq]\[rs]n\[aq]);
> +\&
> +	for (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 (;;) {
> +		struct timespec  timeout;
> +		int  woke;
> +\&
> +		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 (i = 0; i < countof(futexes); ++i) {
> +			if (futexes[i] != waiters[i].val)
> +				printf("%" PRIu32 "%s", futexes[i], i == woke ? "!" : "");

This line goes past the right margin, and could be trivially narrowed
by using the new (C23) fixed-width length modifiers:

	alx@devuan:~/tmp$ cat pf.c 
	#include <stdint.h>
	#include <stdio.h>
	int
	main(void)
	{
		int32_t  i = 7;

		printf("%w32d\n", i);
	}
	alx@devuan:~/tmp$ gcc -Wall -Wextra pf.c 
	alx@devuan:~/tmp$ ./a.out 
	7

So, the line would be:

				printf("%w32d%s", futexes[i], i == woke ? "!" : "");

> +			putchar(\[aq]\[rs]t\[aq]);
> +		}
> +		putchar(\[aq]\[rs]n\[aq]);
> +\&
> +		for (i = 0; i < countof(futexes); ++i)
> +			waiters[i].val = futexes[i];
> +	}
> +}


Have a lovely night!
Alex

-- 
<https://www.alejandro-colomar.es>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [PATCH v10] futex_waitv.2: new page
  2026-02-15 18:18                             ` Alejandro Colomar
@ 2026-02-15 19:00                               ` наб
  2026-02-16  0:32                                 ` Alejandro Colomar
  0 siblings, 1 reply; 53+ messages in thread
From: наб @ 2026-02-15 19:00 UTC (permalink / raw)
  To: Alejandro Colomar; +Cc: linux-man

[-- Attachment #1: Type: text/plain, Size: 13267 bytes --]

Signed-off-by: Ahelenia Ziemiańska <nabijaczleweli@nabijaczleweli.xyz>
---
Range-diff against v9:
1:  76632607a ! 1:  267b3c008 futex_waitv.2: new page
    @@ man/man2/futex_waitv.2 (new)
     +.EX
     +#include <err.h>
     +#include <errno.h>
    -+#include <inttypes.h>
     +#include <linux/futex.h>
     +#include <pthread.h>
     +#include <stdatomic.h>
    @@ man/man2/futex_waitv.2 (new)
     +#include <unistd.h>
     +\&
     +static inline long
    -+my_futex_wait_private(_Atomic uint32_t *uaddr, uint32_t val)
    ++my_futex_wake_private(_Atomic uint32_t *uaddr, uint32_t val)
     +{
     +	return syscall(SYS_futex, uaddr, FUTEX_WAKE_PRIVATE, val);
     +}
    @@ man/man2/futex_waitv.2 (new)
     +		err(EXIT_FAILURE, "getentropy");
     +	init[0] = init[1] = init[2];
     +	for (i = 0; i < countof(futexes); ++i) {
    -+		printf("%" PRIu8 "\[rs]t", init[i]);
    ++		printf("%w8u\[rs]t", init[i]);
     +		atomic_init(&futexes[i], init[i]);
     +		pthread_create(&(pthread_t){}, NULL, worker, &futexes[i]);
     +	}
    @@ man/man2/futex_waitv.2 (new)
     +\&
     +		for (i = 0; i < countof(futexes); ++i) {
     +			if (futexes[i] != waiters[i].val)
    -+				printf("%" PRIu32 "%s", futexes[i], i == woke ? "!" : "");
    ++				printf("%w32u%s", futexes[i], i == woke ? "!" : "");
     +			putchar(\[aq]\[rs]t\[aq]);
     +		}
     +		putchar(\[aq]\[rs]n\[aq]);

 man/man2/futex_waitv.2 | 420 +++++++++++++++++++++++++++++++++++++++++
 man/man7/futex.7       |   9 +-
 2 files changed, 427 insertions(+), 2 deletions(-)
 create mode 100644 man/man2/futex_waitv.2

diff --git u/man/man2/futex_waitv.2 p/man/man2/futex_waitv.2
new file mode 100644
index 000000000..3d11a3379
--- /dev/null
+++ p/man/man2/futex_waitv.2
@@ -0,0 +1,420 @@
+.\" Copyright, the authors of the Linux man-pages project
+.\"
+.\" SPDX-License-Identifier: MIT
+.\"
+.TH futex_waitv 2 (date) "Linux man-pages (unreleased)"
+.SH NAME
+futex_waitv \- wait for FUTEX_WAKE operation on multiple 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 pointed to by the addresses
+.IR waiters []. uaddr
+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 not NULL,
+.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.
+If
+.I timeout
+is NULL,
+the call blocks indefinitely.
+.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 one of
+.BI FUTEX2_SIZE_ *
+ORed with some of the flags below.
+.TP
+.B FUTEX2_SIZE_U32
+.I .val
+and
+.I .uaddr[]
+are 32-bit unsigned integers.
+.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 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.
+.\" commit cec199c5e39bde7191a08087cc3d002ccfab31ff
+.\" Author: Peter Zijlstra <peterz@infradead.org>
+.\" Date:   Wed Apr 16 18:29:16 2025 +0200
+.\"
+.\"     futex: Implement FUTEX2_NUMA
+.\"
+.\" FUTEX2_MPOL is not documented or used anywhere;
+.\" it's unclear to me what it does
+.\" (defined in commit c042c505210dc3453f378df432c10fff3d471bc5
+.\"  "futex: Implement FUTEX2_MPOL")
+.TP
+.B FUTEX2_PRIVATE
+By default, the futex is shared
+.RB "(like " 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
+.BR 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 ,
+.IR FUTEX_WAITV_MAX ].
+.TP
+.B EINVAL
+.I timeout
+was not NULL and
+.I clockid
+was not a valid clock
+.RB ( CLOCK_MONOTONIC
+or
+.BR CLOCK_REALTIME ).
+.TP
+.B EINVAL
+.I *timeout
+is denormal (before epoch or
+.I tv_nsec
+more than 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 is missing a
+.BI FUTEX2_SIZE_ *
+flag or has a size flag different than
+.B FUTEX2_SIZE_U32
+set.
+.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 index of the biggest 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
+.I timeout
+was not NULL and no futex was woken before the timeout elapsed.
+.TP
+.BR EAGAIN " or " EWOULDBLOCK
+The value pointed to by
+.I .uaddr
+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 NOTES
+.BR FUTEX2_SIZE_U8 ,
+.BR FUTEX2_SIZE_U16 ,
+and
+.B FUTEX2_SIZE_U64
+where
+.I .val
+and
+.I .uaddr[]
+are 8, 16, or 64 bits are defined, but not implemented
+.RB ( EINVAL ).
+.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 ().
+.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)] = {};
+	int  i;
+\&
+	if (getentropy(init, sizeof(init)))
+		err(EXIT_FAILURE, "getentropy");
+	init[0] = init[1] = init[2];
+	for (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 (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 (;;) {
+		struct timespec  timeout;
+		int  woke;
+\&
+		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 (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 (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
diff --git u/man/man7/futex.7 p/man/man7/futex.7
index 51c5d5d9b..d271144ff 100644
--- u/man/man7/futex.7
+++ p/man/man7/futex.7
@@ -45,7 +45,9 @@ .SS Semantics
 Any futex operation starts in user space,
 but it may be necessary to communicate with the kernel using the
 .BR futex (2)
-system call.
+or
+.BR futex_waitv (2)
+system calls.
 .P
 To "up" a futex, execute the proper assembler instructions that
 will cause the host CPU to atomically increment the integer.
@@ -72,7 +74,9 @@ .SS Semantics
 .P
 The
 .BR futex (2)
-system call can optionally be passed a timeout specifying how long
+and
+.BR futex_waitv (2)
+system calls can optionally be passed a timeout specifying how long
 the kernel should
 wait for the futex to be upped.
 In this case, semantics are more complex and the programmer is referred
@@ -107,6 +111,7 @@ .SH NOTES
 .SH SEE ALSO
 .BR clone (2),
 .BR futex (2),
+.BR futex_waitv (2),
 .BR get_robust_list (2),
 .BR set_robust_list (2),
 .BR set_tid_address (2),
-- 
2.39.5

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* Re: [PATCH v10] futex_waitv.2: new page
  2026-02-15 19:00                               ` [PATCH v10] " наб
@ 2026-02-16  0:32                                 ` Alejandro Colomar
  2026-02-16 14:20                                   ` [PATCH v11] " наб
  0 siblings, 1 reply; 53+ messages in thread
From: Alejandro Colomar @ 2026-02-16  0:32 UTC (permalink / raw)
  To: наб; +Cc: linux-man

[-- Attachment #1: Type: text/plain, Size: 14531 bytes --]

Hi наб,

(I think we're close to merging.)

On 2026-02-15T20:00:50+0100, наб wrote:
> Signed-off-by: Ahelenia Ziemiańska <nabijaczleweli@nabijaczleweli.xyz>
> ---
[...]
> --- /dev/null
> +++ p/man/man2/futex_waitv.2
> @@ -0,0 +1,420 @@
> +.\" Copyright, the authors of the Linux man-pages project
> +.\"
> +.\" SPDX-License-Identifier: MIT
> +.\"
> +.TH futex_waitv 2 (date) "Linux man-pages (unreleased)"
> +.SH NAME
> +futex_waitv \- wait for FUTEX_WAKE operation on multiple 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[] */

Should we say at .uaddr[0] to be more precise?

> +    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 pointed to by the addresses
> +.IR waiters []. uaddr

Should we maybe say?:

	futex words
	.IR waiters []. uaddr [0]

I'm fine with either; as you prefer.  But this is more specific, which
might help readers a bit.

> +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 not NULL,
> +.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.
> +If
> +.I timeout
> +is NULL,
> +the call blocks indefinitely.

I would reorder the sentences in this paragraph, to simplify:

	If
	.I timeout
	is NULL,
	the call blocks indefinitely.
	Otherwise,
	.I *timeout
	specifies a deadline measured against clock
	.IR clockid .
	This interval ...

This reduces the number of occurences of NULL, which helps find its
meaning.

> +.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 one of
> +.BI FUTEX2_SIZE_ *
> +ORed with some of the flags below.
> +.TP
> +.B FUTEX2_SIZE_U32
> +.I .val
> +and
> +.I .uaddr[]
> +are 32-bit unsigned integers.
> +.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 in an

Maybe 'Except that' would be easier to read?

> +.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.
> +.\" commit cec199c5e39bde7191a08087cc3d002ccfab31ff

Please refer to kernel commits in a single line:

	.\" linux.git cec199c5e39b (2025-05-03; "futex: Implement FUTEX2_NUMA")

The format is documented in
<https://git.kernel.org/pub/scm/docs/man-pages/man-pages.git/tree/CONTRIBUTING.d/patches/trailer#n16>
and there's a git alias to produce them documented in
<https://git.kernel.org/pub/scm/docs/man-pages/man-pages.git/tree/CONTRIBUTING.d/git#n46>

Unless for some reason the commit message is really relevant.

> +.\" Author: Peter Zijlstra <peterz@infradead.org>
> +.\" Date:   Wed Apr 16 18:29:16 2025 +0200
> +.\"
> +.\"     futex: Implement FUTEX2_NUMA
> +.\"
> +.\" FUTEX2_MPOL is not documented or used anywhere;
> +.\" it's unclear to me what it does
> +.\" (defined in commit c042c505210dc3453f378df432c10fff3d471bc5
> +.\"  "futex: Implement FUTEX2_MPOL")
> +.TP
> +.B FUTEX2_PRIVATE
> +By default, the futex is shared
> +.RB "(like " FUTEX_WAIT (2const)),

'(like' is not part of an identifier, nor punctuation; thus, it's better
to have it in a separate line ...

> +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

... like here.

> +.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
> +.BR uintptr_t .

Types should go in italics, not bold.

> +.\"
> +.\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
> +.\"
> +.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 ,
> +.IR FUTEX_WAITV_MAX ].

This is not an expression anymore, but just an macro, so it should be in
bold.

> +.TP
> +.B EINVAL
> +.I timeout
> +was not NULL and
> +.I clockid
> +was not a valid clock
> +.RB ( CLOCK_MONOTONIC
> +or
> +.BR CLOCK_REALTIME ).
> +.TP
> +.B EINVAL
> +.I *timeout
> +is denormal (before epoch or
> +.I tv_nsec
> +more than 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 is missing a
> +.BI FUTEX2_SIZE_ *
> +flag or has a size flag different than
> +.B FUTEX2_SIZE_U32
> +set.
> +.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 index of the biggest NUMA domain

Is biggest the best word here?  I don't have a better idea, but please
have a look at this, just in case.

> +(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
> +.I timeout
> +was not NULL and no futex was woken before the timeout elapsed.

I think this might be simpler and more readable as

	The timeout elapsed and no futex was woken.

If it elapsed, it's obvious that it wasn't NULL.
It could also be written as

	No futex was woken before the timeout elapsed.

I find it slightly more readable in the first form, but if you differ
feel free to pick the second form.

> +.TP
> +.BR EAGAIN " or " EWOULDBLOCK
> +The value pointed to by
> +.I .uaddr

Should we say directly 'The value in .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)).


Cheers,
Alex

> +.\"
> +.\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
> +.\"
> +.SH STANDARDS
> +Linux.
> +.SH NOTES
> +.BR FUTEX2_SIZE_U8 ,
> +.BR FUTEX2_SIZE_U16 ,
> +and
> +.B FUTEX2_SIZE_U64
> +where
> +.I .val
> +and
> +.I .uaddr[]
> +are 8, 16, or 64 bits are defined, but not implemented
> +.RB ( EINVAL ).
> +.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 ().
> +.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)] = {};
> +	int  i;
> +\&
> +	if (getentropy(init, sizeof(init)))
> +		err(EXIT_FAILURE, "getentropy");
> +	init[0] = init[1] = init[2];
> +	for (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 (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 (;;) {
> +		struct timespec  timeout;
> +		int  woke;
> +\&
> +		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 (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 (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
> diff --git u/man/man7/futex.7 p/man/man7/futex.7
> index 51c5d5d9b..d271144ff 100644
> --- u/man/man7/futex.7
> +++ p/man/man7/futex.7
> @@ -45,7 +45,9 @@ .SS Semantics
>  Any futex operation starts in user space,
>  but it may be necessary to communicate with the kernel using the
>  .BR futex (2)
> -system call.
> +or
> +.BR futex_waitv (2)
> +system calls.
>  .P
>  To "up" a futex, execute the proper assembler instructions that
>  will cause the host CPU to atomically increment the integer.
> @@ -72,7 +74,9 @@ .SS Semantics
>  .P
>  The
>  .BR futex (2)
> -system call can optionally be passed a timeout specifying how long
> +and
> +.BR futex_waitv (2)
> +system calls can optionally be passed a timeout specifying how long
>  the kernel should
>  wait for the futex to be upped.
>  In this case, semantics are more complex and the programmer is referred
> @@ -107,6 +111,7 @@ .SH NOTES
>  .SH SEE ALSO
>  .BR clone (2),
>  .BR futex (2),
> +.BR futex_waitv (2),
>  .BR get_robust_list (2),
>  .BR set_robust_list (2),
>  .BR set_tid_address (2),
> -- 
> 2.39.5



-- 
<https://www.alejandro-colomar.es>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [PATCH v11] futex_waitv.2: new page
  2026-02-16  0:32                                 ` Alejandro Colomar
@ 2026-02-16 14:20                                   ` наб
  2026-02-16 14:50                                     ` Alejandro Colomar
  0 siblings, 1 reply; 53+ messages in thread
From: наб @ 2026-02-16 14:20 UTC (permalink / raw)
  To: Alejandro Colomar; +Cc: linux-man

[-- Attachment #1: Type: text/plain, Size: 16146 bytes --]

On Mon, Feb 16, 2026 at 01:32:29AM +0100, Alejandro Colomar wrote:
> On 2026-02-15T20:00:50+0100, наб wrote:
> > +struct futex_waitv {
> > +    u64 val;        /* Expected value at \f[I]uaddr\f[] */
> Should we say at .uaddr[0] to be more precise?
I think in general it's between "of *pointer" or "at pointer",
and "Expected value of .uaddr[0]/*.uaddr" read really poorly to me.

> > +This operation tests that the values at the
> > +futex words pointed to by the addresses
> > +.IR waiters []. uaddr
> 
> Should we maybe say?:
> 
> 	futex words
> 	.IR waiters []. uaddr [0]

It does read unwieldy, but I think that's too cut-down...

	futex words at
	.IR waiters []. uaddr


> > +If the NUMA word is
> > +.BR FUTEX_NO_NODE ,
> > +the node number of the processor the syscall executes on is written to it.
> > +(Except in an
> Maybe 'Except that' would be easier to read?
I don't think that works, but maybe "Except for an"...

Scissor-patch below.

Best,
-- >8 --
Subject: [PATCH v11] futex_waitv.2: new page

Signed-off-by: Ahelenia Ziemiańska <nabijaczleweli@nabijaczleweli.xyz>
---
Range-diff against v10:
1:  267b3c008 ! 1:  39abafa84 futex_waitv.2: new page
    @@ man/man2/futex_waitv.2 (new)
     +.P
     +If
     +.I timeout
    -+is not NULL,
    ++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.
    -+If
    -+.I timeout
    -+is NULL,
    -+the call blocks indefinitely.
     +.P
     +Futex words to monitor are given by
     +.IR "struct futex_waitv" ,
    @@ man/man2/futex_waitv.2 (new)
     +If the NUMA word is
     +.BR FUTEX_NO_NODE ,
     +the node number of the processor the syscall executes on is written to it.
    -+(Except in an
    ++(Except for an
     +.B EINVAL
     +or
     +.B EFAULT
    @@ man/man2/futex_waitv.2 (new)
     +.IP
     +Futexes are placed on the NUMA node given by the NUMA word.
     +Futexes without this flag are placed on a random node.
    -+.\" commit cec199c5e39bde7191a08087cc3d002ccfab31ff
    -+.\" Author: Peter Zijlstra <peterz@infradead.org>
    -+.\" Date:   Wed Apr 16 18:29:16 2025 +0200
    -+.\"
    -+.\"     futex: Implement FUTEX2_NUMA
    ++.\" 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 commit c042c505210dc3453f378df432c10fff3d471bc5
    -+.\"  "futex: Implement FUTEX2_MPOL")
    ++.\" 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
    -+.RB "(like " FUTEX_WAIT (2const)),
    ++(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
    @@ man/man2/futex_waitv.2 (new)
     +Programs should assign to
     +.I .uaddr
     +by casting a pointer to
    -+.BR uintptr_t .
    ++.IR uintptr_t .
     +.\"
     +.\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
     +.\"
    @@ man/man2/futex_waitv.2 (new)
     +.I n
     +was not in the range
     +.RB [ 1 ,
    -+.IR FUTEX_WAITV_MAX ].
    ++.BR FUTEX_WAITV_MAX ].
     +.TP
     +.B EINVAL
     +.I timeout
    @@ man/man2/futex_waitv.2 (new)
     +.IR waiters []. flags ,
     +and the NUMA word
     +(which is the same size as the futex word)
    -+is too small to contain the index of the biggest NUMA domain
    ++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).
    @@ man/man2/futex_waitv.2 (new)
     +.BR FUTEX_NO_NODE .
     +.TP
     +.B ETIMEDOUT
    -+.I timeout
    -+was not NULL and no futex was woken before the timeout elapsed.
    ++The timeout elapsed before any futex was woken.
     +.TP
     +.BR EAGAIN " or " EWOULDBLOCK
    -+The value pointed to by
    -+.I .uaddr
    ++The value in
    ++.I .uaddr[0]
     +was not equal to the expected value
     +.I .val
     +at the time of the call.

 man/man2/futex_waitv.2 | 413 +++++++++++++++++++++++++++++++++++++++++
 man/man7/futex.7       |   9 +-
 2 files changed, 420 insertions(+), 2 deletions(-)
 create mode 100644 man/man2/futex_waitv.2

diff --git u/man/man2/futex_waitv.2 p/man/man2/futex_waitv.2
new file mode 100644
index 000000000..f26c10656
--- /dev/null
+++ p/man/man2/futex_waitv.2
@@ -0,0 +1,413 @@
+.\" Copyright, the authors of the Linux man-pages project
+.\"
+.\" SPDX-License-Identifier: MIT
+.\"
+.TH futex_waitv 2 (date) "Linux man-pages (unreleased)"
+.SH NAME
+futex_waitv \- wait for FUTEX_WAKE operation on multiple 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 pointed to by the addresses
+.IR waiters []. uaddr
+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
+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 one of
+.BI FUTEX2_SIZE_ *
+ORed with some of the flags below.
+.TP
+.B FUTEX2_SIZE_U32
+.I .val
+and
+.I .uaddr[]
+are 32-bit unsigned integers.
+.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 for 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
+.RB ( CLOCK_MONOTONIC
+or
+.BR CLOCK_REALTIME ).
+.TP
+.B EINVAL
+.I *timeout
+is denormal (before epoch or
+.I tv_nsec
+more than 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 is missing a
+.BI FUTEX2_SIZE_ *
+flag or has a size flag different than
+.B FUTEX2_SIZE_U32
+set.
+.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 NOTES
+.BR FUTEX2_SIZE_U8 ,
+.BR FUTEX2_SIZE_U16 ,
+and
+.B FUTEX2_SIZE_U64
+where
+.I .val
+and
+.I .uaddr[]
+are 8, 16, or 64 bits are defined, but not implemented
+.RB ( EINVAL ).
+.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 ().
+.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)] = {};
+	int  i;
+\&
+	if (getentropy(init, sizeof(init)))
+		err(EXIT_FAILURE, "getentropy");
+	init[0] = init[1] = init[2];
+	for (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 (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 (;;) {
+		struct timespec  timeout;
+		int  woke;
+\&
+		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 (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 (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
diff --git u/man/man7/futex.7 p/man/man7/futex.7
index 51c5d5d9b..d271144ff 100644
--- u/man/man7/futex.7
+++ p/man/man7/futex.7
@@ -45,7 +45,9 @@ .SS Semantics
 Any futex operation starts in user space,
 but it may be necessary to communicate with the kernel using the
 .BR futex (2)
-system call.
+or
+.BR futex_waitv (2)
+system calls.
 .P
 To "up" a futex, execute the proper assembler instructions that
 will cause the host CPU to atomically increment the integer.
@@ -72,7 +74,9 @@ .SS Semantics
 .P
 The
 .BR futex (2)
-system call can optionally be passed a timeout specifying how long
+and
+.BR futex_waitv (2)
+system calls can optionally be passed a timeout specifying how long
 the kernel should
 wait for the futex to be upped.
 In this case, semantics are more complex and the programmer is referred
@@ -107,6 +111,7 @@ .SH NOTES
 .SH SEE ALSO
 .BR clone (2),
 .BR futex (2),
+.BR futex_waitv (2),
 .BR get_robust_list (2),
 .BR set_robust_list (2),
 .BR set_tid_address (2),
-- 
2.39.5


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* Re: [PATCH v11] futex_waitv.2: new page
  2026-02-16 14:20                                   ` [PATCH v11] " наб
@ 2026-02-16 14:50                                     ` Alejandro Colomar
  2026-02-16 20:43                                       ` [PATCH v12] " наб
  0 siblings, 1 reply; 53+ messages in thread
From: Alejandro Colomar @ 2026-02-16 14:50 UTC (permalink / raw)
  To: наб; +Cc: linux-man

[-- Attachment #1: Type: text/plain, Size: 2014 bytes --]

Hi,

On 2026-02-16T15:20:49+0100, наб wrote:
> On Mon, Feb 16, 2026 at 01:32:29AM +0100, Alejandro Colomar wrote:
> > On 2026-02-15T20:00:50+0100, наб wrote:
> > > +struct futex_waitv {
> > > +    u64 val;        /* Expected value at \f[I]uaddr\f[] */
> > Should we say at .uaddr[0] to be more precise?
> I think in general it's between "of *pointer" or "at pointer",
> and "Expected value of .uaddr[0]/*.uaddr" read really poorly to me.
> 
> > > +This operation tests that the values at the
> > > +futex words pointed to by the addresses
> > > +.IR waiters []. uaddr
> > 
> > Should we maybe say?:
> > 
> > 	futex words
> > 	.IR waiters []. uaddr [0]
> 
> It does read unwieldy, but I think that's too cut-down...
> 
> 	futex words at
> 	.IR waiters []. uaddr

I prefer "futex words pointed to by the addresses" over "futex words at".
My point was that the explicit [0] might be more readable.

> 
> 
> > > +If the NUMA word is
> > > +.BR FUTEX_NO_NODE ,
> > > +the node number of the processor the syscall executes on is written to it.
> > > +(Except in an
> > Maybe 'Except that' would be easier to read?
> I don't think that works, but maybe "Except for an"...

Sorry; I didn't explain myself well.  I meant 'Except that in an'.

> 
> Scissor-patch below.
> 
> Best,
> -- >8 --
> Subject: [PATCH v11] futex_waitv.2: new page
> 
> Signed-off-by: Ahelenia Ziemiańska <nabijaczleweli@nabijaczleweli.xyz>
> ---
> Range-diff against v10:
> 1:  267b3c008 ! 1:  39abafa84 futex_waitv.2: new page
[...]
>     @@ man/man2/futex_waitv.2 (new)
>      +If the NUMA word is
>      +.BR FUTEX_NO_NODE ,
>      +the node number of the processor the syscall executes on is written to it.
>     -+(Except in an
>     ++(Except for an

I would still insert the omitted 'that' here.  I have no preference
regarding 'for' vs 'in'.

>      +.B EINVAL
>      +or
>      +.B EFAULT
[...]


Have a lovely day!
Alex

-- 
<https://www.alejandro-colomar.es>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [PATCH v12] futex_waitv.2: new page
  2026-02-16 14:50                                     ` Alejandro Colomar
@ 2026-02-16 20:43                                       ` наб
  2026-02-17 13:07                                         ` Alejandro Colomar
  0 siblings, 1 reply; 53+ messages in thread
From: наб @ 2026-02-16 20:43 UTC (permalink / raw)
  To: Alejandro Colomar; +Cc: linux-man

[-- Attachment #1: Type: text/plain, Size: 12459 bytes --]

Signed-off-by: Ahelenia Ziemiańska <nabijaczleweli@nabijaczleweli.xyz>
---
Range-diff against v11:
1:  39abafa84 ! 1:  ac211c922 futex_waitv.2: new page
    @@ man/man2/futex_waitv.2 (new)
     +for general minutiae of futex waiting, see the page above.
     +.P
     +This operation tests that the values at the
    -+futex words pointed to by the addresses
    -+.IR waiters []. uaddr
    ++futex words
    ++.IR waiters []. uaddr[0]
     +still contain respective expected values
     +.IR waiters []. val ,
     +and if so, sleeps waiting for a
    @@ man/man2/futex_waitv.2 (new)
     +If the NUMA word is
     +.BR FUTEX_NO_NODE ,
     +the node number of the processor the syscall executes on is written to it.
    -+(Except for an
    ++(Except that in an
     +.B EINVAL
     +or
     +.B EFAULT

 man/man2/futex_waitv.2 | 413 +++++++++++++++++++++++++++++++++++++++++
 man/man7/futex.7       |   9 +-
 2 files changed, 420 insertions(+), 2 deletions(-)
 create mode 100644 man/man2/futex_waitv.2

diff --git u/man/man2/futex_waitv.2 p/man/man2/futex_waitv.2
new file mode 100644
index 000000000..9790779ab
--- /dev/null
+++ p/man/man2/futex_waitv.2
@@ -0,0 +1,413 @@
+.\" Copyright, the authors of the Linux man-pages project
+.\"
+.\" SPDX-License-Identifier: MIT
+.\"
+.TH futex_waitv 2 (date) "Linux man-pages (unreleased)"
+.SH NAME
+futex_waitv \- wait for FUTEX_WAKE operation on multiple 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
+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 one of
+.BI FUTEX2_SIZE_ *
+ORed with some of the flags below.
+.TP
+.B FUTEX2_SIZE_U32
+.I .val
+and
+.I .uaddr[]
+are 32-bit unsigned integers.
+.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
+.RB ( CLOCK_MONOTONIC
+or
+.BR CLOCK_REALTIME ).
+.TP
+.B EINVAL
+.I *timeout
+is denormal (before epoch or
+.I tv_nsec
+more than 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 is missing a
+.BI FUTEX2_SIZE_ *
+flag or has a size flag different than
+.B FUTEX2_SIZE_U32
+set.
+.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 NOTES
+.BR FUTEX2_SIZE_U8 ,
+.BR FUTEX2_SIZE_U16 ,
+and
+.B FUTEX2_SIZE_U64
+where
+.I .val
+and
+.I .uaddr[]
+are 8, 16, or 64 bits are defined, but not implemented
+.RB ( EINVAL ).
+.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 ().
+.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)] = {};
+	int  i;
+\&
+	if (getentropy(init, sizeof(init)))
+		err(EXIT_FAILURE, "getentropy");
+	init[0] = init[1] = init[2];
+	for (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 (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 (;;) {
+		struct timespec  timeout;
+		int  woke;
+\&
+		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 (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 (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
diff --git u/man/man7/futex.7 p/man/man7/futex.7
index 51c5d5d9b..d271144ff 100644
--- u/man/man7/futex.7
+++ p/man/man7/futex.7
@@ -45,7 +45,9 @@ .SS Semantics
 Any futex operation starts in user space,
 but it may be necessary to communicate with the kernel using the
 .BR futex (2)
-system call.
+or
+.BR futex_waitv (2)
+system calls.
 .P
 To "up" a futex, execute the proper assembler instructions that
 will cause the host CPU to atomically increment the integer.
@@ -72,7 +74,9 @@ .SS Semantics
 .P
 The
 .BR futex (2)
-system call can optionally be passed a timeout specifying how long
+and
+.BR futex_waitv (2)
+system calls can optionally be passed a timeout specifying how long
 the kernel should
 wait for the futex to be upped.
 In this case, semantics are more complex and the programmer is referred
@@ -107,6 +111,7 @@ .SH NOTES
 .SH SEE ALSO
 .BR clone (2),
 .BR futex (2),
+.BR futex_waitv (2),
 .BR get_robust_list (2),
 .BR set_robust_list (2),
 .BR set_tid_address (2),
-- 
2.39.5

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* Re: [PATCH v12] futex_waitv.2: new page
  2026-02-16 20:43                                       ` [PATCH v12] " наб
@ 2026-02-17 13:07                                         ` Alejandro Colomar
  2026-02-17 14:31                                           ` [PATCH v13] " наб
  0 siblings, 1 reply; 53+ messages in thread
From: Alejandro Colomar @ 2026-02-17 13:07 UTC (permalink / raw)
  To: наб; +Cc: linux-man

[-- Attachment #1: Type: text/plain, Size: 1063 bytes --]

Hi,

On 2026-02-16T21:43:14+0100, наб wrote:
> Signed-off-by: Ahelenia Ziemiańska <nabijaczleweli@nabijaczleweli.xyz>
> ---
> Range-diff against v11:
[...]
> 
>  man/man2/futex_waitv.2 | 413 +++++++++++++++++++++++++++++++++++++++++
>  man/man7/futex.7       |   9 +-
>  2 files changed, 420 insertions(+), 2 deletions(-)
>  create mode 100644 man/man2/futex_waitv.2
> 
> diff --git u/man/man2/futex_waitv.2 p/man/man2/futex_waitv.2
> new file mode 100644
> index 000000000..9790779ab
> --- /dev/null
> +++ p/man/man2/futex_waitv.2
> @@ -0,0 +1,413 @@
[...]
> +.TP
> +.B EINVAL
> +.I *timeout
> +is denormal (before epoch or
> +.I tv_nsec
> +more than 999\[aq]999\[aq]999).

I suspect .tv_nsec < 0 is also denormal, and it being a long, that's
certainly possible, right?  Should we specify a range?

The rest looks good to me; I think this is the last question.

(I'll still apply some editorial changes, but don't want to bother you
 more with them.)


Have a lovely day!
Alex

-- 
<https://www.alejandro-colomar.es>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [PATCH v13] futex_waitv.2: new page
  2026-02-17 13:07                                         ` Alejandro Colomar
@ 2026-02-17 14:31                                           ` наб
  2026-02-17 15:46                                             ` Alejandro Colomar
  0 siblings, 1 reply; 53+ messages in thread
From: наб @ 2026-02-17 14:31 UTC (permalink / raw)
  To: Alejandro Colomar; +Cc: linux-man

[-- Attachment #1: Type: text/plain, Size: 13847 bytes --]

On Tue, Feb 17, 2026 at 02:07:42PM +0100, Alejandro Colomar wrote:
> On 2026-02-16T21:43:14+0100, наб wrote:
> > +.TP
> > +.B EINVAL
> > +.I *timeout
> > +is denormal (before epoch or
> > +.I tv_nsec
> > +more than 999\[aq]999\[aq]999).
> I suspect .tv_nsec < 0 is also denormal, and it being a long, that's
> certainly possible, right?  Should we specify a range?
Hm, yeah:
	SYSCALL_DEFINE5(futex_waitv, struct futex_waitv __user *, waiters,
			unsigned int, nr_futexes, unsigned int, flags,
			struct __kernel_timespec __user *, timeout, clockid_t, clockid)
	{
		// ...
		if (timeout && (ret = futex2_setup_timeout(timeout, clockid, &to)))
			return ret;

	static int futex2_setup_timeout(struct __kernel_timespec __user *timeout,
					clockid_t clockid, struct hrtimer_sleeper *to)
	{
		// ...
		if (get_timespec64(&ts, timeout))
			return -EFAULT;

		/*
		 * Since there's no opcode for futex_waitv, use
		 * FUTEX_WAIT_BITSET that uses absolute timeout as well
		 */
		ret = futex_init_timeout(FUTEX_WAIT_BITSET, flag_init, &ts, &time);
		if (ret)
			return ret;

	static __always_inline int
	futex_init_timeout(u32 cmd, u32 op, struct timespec64 *ts, ktime_t *t)
	{
		if (!timespec64_valid(ts))
			return -EINVAL;

	/*
	 * Returns true if the timespec64 is norm, false if denorm:
	 */
	static inline bool timespec64_valid(const struct timespec64 *ts)
	{
		/* Dates before 1970 are bogus */
		if (ts->tv_sec < 0)
			return false;
		/* Can't have more nanoseconds then a second */
		if ((unsigned long)ts->tv_nsec >= NSEC_PER_SEC)
			return false;
		return true;
	}
I hadn't considered the cast also checks for <0.

Scissor-patch below.

Best,
-- >8 --
Subject: [PATCH v13] futex_waitv.2: new page

Signed-off-by: Ahelenia Ziemiańska <nabijaczleweli@nabijaczleweli.xyz>
---
Range-diff against v12:
1:  ac211c922 ! 1:  c6fc6c4a7 futex_waitv.2: new page
    @@ man/man2/futex_waitv.2 (new)
     +.I *timeout
     +is denormal (before epoch or
     +.I tv_nsec
    -+more than 999\[aq]999\[aq]999).
    ++not in the range
    ++.RB [ 0 ,
    ++.BR 999\[aq]999\[aq]999 ]).
     +.TP
     +.B EINVAL
     +Any

 man/man2/futex_waitv.2 | 415 +++++++++++++++++++++++++++++++++++++++++
 man/man7/futex.7       |   9 +-
 2 files changed, 422 insertions(+), 2 deletions(-)
 create mode 100644 man/man2/futex_waitv.2

diff --git u/man/man2/futex_waitv.2 p/man/man2/futex_waitv.2
new file mode 100644
index 000000000..f0553698e
--- /dev/null
+++ p/man/man2/futex_waitv.2
@@ -0,0 +1,415 @@
+.\" Copyright, the authors of the Linux man-pages project
+.\"
+.\" SPDX-License-Identifier: MIT
+.\"
+.TH futex_waitv 2 (date) "Linux man-pages (unreleased)"
+.SH NAME
+futex_waitv \- wait for FUTEX_WAKE operation on multiple 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
+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 one of
+.BI FUTEX2_SIZE_ *
+ORed with some of the flags below.
+.TP
+.B FUTEX2_SIZE_U32
+.I .val
+and
+.I .uaddr[]
+are 32-bit unsigned integers.
+.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
+.RB ( CLOCK_MONOTONIC
+or
+.BR CLOCK_REALTIME ).
+.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 is missing a
+.BI FUTEX2_SIZE_ *
+flag or has a size flag different than
+.B FUTEX2_SIZE_U32
+set.
+.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 NOTES
+.BR FUTEX2_SIZE_U8 ,
+.BR FUTEX2_SIZE_U16 ,
+and
+.B FUTEX2_SIZE_U64
+where
+.I .val
+and
+.I .uaddr[]
+are 8, 16, or 64 bits are defined, but not implemented
+.RB ( EINVAL ).
+.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 ().
+.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)] = {};
+	int  i;
+\&
+	if (getentropy(init, sizeof(init)))
+		err(EXIT_FAILURE, "getentropy");
+	init[0] = init[1] = init[2];
+	for (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 (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 (;;) {
+		struct timespec  timeout;
+		int  woke;
+\&
+		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 (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 (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
diff --git u/man/man7/futex.7 p/man/man7/futex.7
index 51c5d5d9b..d271144ff 100644
--- u/man/man7/futex.7
+++ p/man/man7/futex.7
@@ -45,7 +45,9 @@ .SS Semantics
 Any futex operation starts in user space,
 but it may be necessary to communicate with the kernel using the
 .BR futex (2)
-system call.
+or
+.BR futex_waitv (2)
+system calls.
 .P
 To "up" a futex, execute the proper assembler instructions that
 will cause the host CPU to atomically increment the integer.
@@ -72,7 +74,9 @@ .SS Semantics
 .P
 The
 .BR futex (2)
-system call can optionally be passed a timeout specifying how long
+and
+.BR futex_waitv (2)
+system calls can optionally be passed a timeout specifying how long
 the kernel should
 wait for the futex to be upped.
 In this case, semantics are more complex and the programmer is referred
@@ -107,6 +111,7 @@ .SH NOTES
 .SH SEE ALSO
 .BR clone (2),
 .BR futex (2),
+.BR futex_waitv (2),
 .BR get_robust_list (2),
 .BR set_robust_list (2),
 .BR set_tid_address (2),
-- 
2.39.5


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* Re: [PATCH v13] futex_waitv.2: new page
  2026-02-17 14:31                                           ` [PATCH v13] " наб
@ 2026-02-17 15:46                                             ` Alejandro Colomar
  2026-02-17 16:17                                               ` наб
  0 siblings, 1 reply; 53+ messages in thread
From: Alejandro Colomar @ 2026-02-17 15:46 UTC (permalink / raw)
  To: наб; +Cc: linux-man

[-- Attachment #1: Type: text/plain, Size: 5522 bytes --]

Hi,

On 2026-02-17T15:31:29+0100, наб wrote:
> On Tue, Feb 17, 2026 at 02:07:42PM +0100, Alejandro Colomar wrote:
> > On 2026-02-16T21:43:14+0100, наб wrote:
> > > +.TP
> > > +.B EINVAL
> > > +.I *timeout
> > > +is denormal (before epoch or
> > > +.I tv_nsec
> > > +more than 999\[aq]999\[aq]999).
> > I suspect .tv_nsec < 0 is also denormal, and it being a long, that's
> > certainly possible, right?  Should we specify a range?
> Hm, yeah:
[...]
> I hadn't considered the cast also checks for <0.
> 
> Scissor-patch below.

I'm applying the patch, amended with the following.

	diff --git c/man/man2/futex_waitv.2 i/man/man2/futex_waitv.2
	index f0553698e42a..7e336fec830d 100644
	--- c/man/man2/futex_waitv.2
	+++ i/man/man2/futex_waitv.2
	@@ -4,7 +4,7 @@
	 .\"
	 .TH futex_waitv 2 (date) "Linux man-pages (unreleased)"
	 .SH NAME
	-futex_waitv \- wait for FUTEX_WAKE operation on multiple futexes
	+futex_waitv \- wait for FUTEX_WAKE operation on a vector of futexes

The NAME section should try to explain the name.  Thus, using the word
'vector' in the description of the name seems better, as it clarifies
why it has a trailing 'v'.

	 .SH LIBRARY
	 Standard C library
	 .RI ( libc ,\~ \-lc )
	@@ -15,20 +15,20 @@ .SH SYNOPSIS
	 .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 ");"
	+.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 );

I'm planning a global change in SYNOPSIS to use SY/YS.  This will reduce
the work.

	 .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 */
	+       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 */

I used a tab.  Also, two spaces between type and name, and between code
and comment.

	 };
	 .EE
	 .SH DESCRIPTION
	@@ -300,6 +300,7 @@ .SH EXAMPLES
	 .B !\&
	 indicates the futex that woke up each
	 .BR futex_waitv ().
	+.P

You forgot the P.

	 .in +4
	 .EX
	 .RB $\~ ./futex_waitv
	@@ -361,29 +362,28 @@ .SH EXAMPLES
	 int
	 main(void)
	 {
	-       _Atomic uint32_t  futexes[10];
	-       uint8_t  init[countof(futexes)];
	-       struct futex_waitv waiters[countof(futexes)] = {};
	-       int  i;
	+       _Atomic uint32_t    futexes[10];
	+       uint8_t             init[countof(futexes)];
	+       struct futex_waitv  waiters[countof(futexes)] = {};

Aligned the names.

	 \&
		if (getentropy(init, sizeof(init)))
			err(EXIT_FAILURE, "getentropy");
		init[0] = init[1] = init[2];
	-       for (i = 0; i < countof(futexes); ++i) {
	+       for (int i = 0; i < countof(futexes); ++i) {

I prefer C99 loop variables.

			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 (i = 0; i < countof(futexes); ++i) {
	+       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;
	-               int  woke;
	 \&
			clock_gettime(CLOCK_MONOTONIC, &timeout);
			timeout.tv_sec += 1;
	@@ -392,14 +392,14 @@ .SH EXAMPLES
			if (woke == \-1 && (errno != EAGAIN && errno != EWOULDBLOCK))
				err(EXIT_FAILURE, "my_futex_waitv");
	 \&
	-               for (i = 0; i < countof(futexes); ++i) {
	+               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 (i = 0; i < countof(futexes); ++i)
	+               for (int i = 0; i < countof(futexes); ++i)
				waiters[i].val = futexes[i];
		}
	 }

I see some diagnostics from Clang, which I suspect I should just
silence; let me know your opinion:

	.tmp/man/man2/futex_waitv.2.d/futex_waitv.c:36:13: error: implicit use of sequentially-consistent atomic may incur stronger memory barriers than necessary [clang-diagnostic-atomic-implicit-seq-cst,-warnings-as-errors]
	   36 |      usleep(*futex * 10000);
	      |             ^


	.tmp/man/man2/futex_waitv.2.d/futex_waitv.c:37:13: error: implicit use of sequentially-consistent atomic may incur stronger memory barriers than necessary [clang-diagnostic-atomic-implicit-seq-cst,-warnings-as-errors]
	   37 |      *futex *= 2;
	      |             ^

I have little knowledge about atomics, so can't judge, but the code
seems good to me.  Please confirm.


Cheers,
Alex

-- 
<https://www.alejandro-colomar.es>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v13] futex_waitv.2: new page
  2026-02-17 15:46                                             ` Alejandro Colomar
@ 2026-02-17 16:17                                               ` наб
  2026-02-18  0:31                                                 ` Alejandro Colomar
  0 siblings, 1 reply; 53+ messages in thread
From: наб @ 2026-02-17 16:17 UTC (permalink / raw)
  To: Alejandro Colomar; +Cc: linux-man

[-- Attachment #1: Type: text/plain, Size: 995 bytes --]

On Tue, Feb 17, 2026 at 04:46:28PM +0100, Alejandro Colomar wrote:
> silence; let me know your opinion:
> 
> 	.tmp/man/man2/futex_waitv.2.d/futex_waitv.c:36:13: error: implicit use of sequentially-consistent atomic may incur stronger memory barriers than necessary [clang-diagnostic-atomic-implicit-seq-cst,-warnings-as-errors]
> 	   36 |      usleep(*futex * 10000);
> 	      |             ^
> 
> 
> 	.tmp/man/man2/futex_waitv.2.d/futex_waitv.c:37:13: error: implicit use of sequentially-consistent atomic may incur stronger memory barriers than necessary [clang-diagnostic-atomic-implicit-seq-cst,-warnings-as-errors]
> 	   37 |      *futex *= 2;
> 	      |             ^
> 
> I have little knowledge about atomics, so can't judge, but the code
> seems good to me.  Please confirm.
Yeah, that's okay. Worst-case is it'll be marginally slower than
something more verbose that specifies... acqrel? which would be minimal here.
But that doesn't matter for example code.

Best,

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v13] futex_waitv.2: new page
  2026-02-17 16:17                                               ` наб
@ 2026-02-18  0:31                                                 ` Alejandro Colomar
  0 siblings, 0 replies; 53+ messages in thread
From: Alejandro Colomar @ 2026-02-18  0:31 UTC (permalink / raw)
  To: наб; +Cc: linux-man

[-- Attachment #1: Type: text/plain, Size: 1269 bytes --]

Hi наб,

On 2026-02-17T17:17:03+0100, наб wrote:
> On Tue, Feb 17, 2026 at 04:46:28PM +0100, Alejandro Colomar wrote:
> > silence; let me know your opinion:
> > 
> > 	.tmp/man/man2/futex_waitv.2.d/futex_waitv.c:36:13: error: implicit use of sequentially-consistent atomic may incur stronger memory barriers than necessary [clang-diagnostic-atomic-implicit-seq-cst,-warnings-as-errors]
> > 	   36 |      usleep(*futex * 10000);
> > 	      |             ^
> > 
> > 
> > 	.tmp/man/man2/futex_waitv.2.d/futex_waitv.c:37:13: error: implicit use of sequentially-consistent atomic may incur stronger memory barriers than necessary [clang-diagnostic-atomic-implicit-seq-cst,-warnings-as-errors]
> > 	   37 |      *futex *= 2;
> > 	      |             ^
> > 
> > I have little knowledge about atomics, so can't judge, but the code
> > seems good to me.  Please confirm.
> Yeah, that's okay. Worst-case is it'll be marginally slower than
> something more verbose that specifies... acqrel? which would be minimal here.
> But that doesn't matter for example code.

Thanks!  Applied and pushed.  In a moment, I'll send a patch to refactor
the page, to see what you think of it.


Have a lovely night!
Alex

-- 
<https://www.alejandro-colomar.es>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [PATCH v1 0/1] futex_waitv.2: Move text to a new PARAMETERS section
  2026-02-07 12:49 [PATCH] futex_waitv.2: new page наб
  2026-02-07 18:57 ` Alejandro Colomar
  2026-02-07 22:00 ` [PATCH v2] " наб
@ 2026-02-18  0:41 ` Alejandro Colomar
  2026-02-18  0:41   ` [PATCH v1 1/1] man/man2/futex_waitv.2: " Alejandro Colomar
  2026-02-18 20:16   ` [PATCH v1 0/1] futex_waitv.2: " наб
  2 siblings, 2 replies; 53+ messages in thread
From: Alejandro Colomar @ 2026-02-18  0:41 UTC (permalink / raw)
  To: linux-man; +Cc: Alejandro Colomar, наб

Hi!

Please let me know what you think of this patch?
Here's how the patch changes the page:

	$ MANWIDTH=72 diffman-git -U10 HEAD
	--- HEAD^:man/man2/futex_waitv.2
	+++ HEAD:man/man2/futex_waitv.2
	@@ -37,57 +37,103 @@ DESCRIPTION
	      ers[].uaddr[0] still contain respective expected values wait‐
	      ers[].val, and if so, sleeps waiting for a FUTEX_WAKE(2const) op‐
	      eration on any of the futex words, and returns the index of a
	      waiter whose futex was woken.
	 
	      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 waiters[].val, the call fails immediately with the er‐
	      ror EAGAIN.
	 
	-     If timeout is NULL, the call blocks indefinitely.  Otherwise,
	-     *timeout specifies a deadline measured against clock clockid.
	-     This interval will be rounded up to the system clock granularity,
	-     and is guaranteed not to expire early.
	-
	-     Futex words to monitor are given by struct futex_waitv, whose
	-     fields are analogous to FUTEX_WAIT(2const) parameters, except
	-     .__reserved must be 0 and .flags must contain one of FUTEX2_SIZE_*
	-     ORed with some of the flags below.
	-
	-     FUTEX2_SIZE_U32
	-            .val and .uaddr[] are 32‐bit unsigned integers.
	-
	-     FUTEX2_NUMA
	-            The futex word is followed by another word of the same size
	-            (.uaddr points to uintN_t[2] rather than uintN_t.  The word
	-            is given by .uaddr[1]), which can be either FUTEX_NO_NODE
	-            (all bits set) or a NUMA node number.
	-
	-            If the NUMA word is FUTEX_NO_NODE, the node number of the
	-            processor the syscall executes on is written to it.  (Ex‐
	-            cept that in an EINVAL or EFAULT condition, this happens to
	-            all waiters whose .flags have FUTEX2_NUMA set.)
	-
	-            Futexes are placed on the NUMA node given by the NUMA word.
	-            Futexes without this flag are placed on a random node.
	-
	-     FUTEX2_PRIVATE
	-            By default, the futex is shared (like 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 FUTEX_WAIT_PRIVATE; this most of‐
	-            ten means they are part of the same process).  Private fu‐
	-            texes are faster than shared ones.
	+PARAMETERS
	+   futex_waitv()
	+     waiters
	+            Futex words to monitor are given by struct futex_waitv,
	+            whose fields are similar to FUTEX_WAIT(2const) parameters.
	+
	+     n      Specify as countof(waiters).
	+
	+     timeout
	+            If timeout is NULL, the call blocks indefinitely.  Other‐
	+            wise, *timeout specifies a deadline measured against clock
	+            clockid.  This interval will be rounded up to the system
	+            clock granularity, and is guaranteed not to expire early.
	+
	+     clockid
	+            Supported clocks:
	+
	+            CLOCK_MONOTONIC
	+            CLOCK_REALTIME
	+
	+     flags  Specify as 0.
	+
	+   struct futex_waitv
	+     .val   Expected value of .uaddr[0].
	+
	+     .uaddr
	+
	+            .uaddr[0]
	+                   See FUTEX_WAIT(2const).
	+
	+            .uaddr[1]
	+                   See FUTEX2_NUMA below.  This field, if it exists,
	+                   must contain a NUMA word, which can be either FU‐
	+                   TEX_NO_NODE or a NUMA node number.
	+
	+                   If the NUMA word is FUTEX_NO_NODE, the node number
	+                   of the processor the syscall executes on is written
	+                   to it.  (Except that in an EINVAL or EFAULT condi‐
	+                   tion, this happens to all waiters whose .flags have
	+                   FUTEX2_NUMA set.)
	+
	+            Programs should assign to .uaddr by casting a pointer to
	+            uintptr_t.
	+
	+     .flags
	+            It must contain exactly one size flag ORed with zero or
	+            more of the other flags.
	+
	+            Size flags
	+
	+                   FUTEX2_SIZE_U32
	+                          .val and .uaddr[] are 32‐bit unsigned inte‐
	+                          gers.
	+
	+                   FUTEX2_SIZE_U8
	+                   FUTEX2_SIZE_U16
	+                   FUTEX2_SIZE_U64
	+                          These are defined, but not supported (EIN‐
	+                          VAL).
	+
	+            Other flags
	+
	+                   FUTEX2_NUMA
	+                          The futex word is followed by the NUMA word,
	+                          of the same size (.uaddr points to uintN_t[2]
	+                          rather than uintN_t).
	+
	+                          Futexes are placed on the NUMA node given by
	+                          the NUMA word.  Futexes without this flag are
	+                          placed on a random node.
	+
	+                   FUTEX2_PRIVATE
	+                          By default, the futex is shared (like FU‐
	+                          TEX_WAIT(2const)), and can be accessed by
	+                          multiple processes; this flag waits on a pri‐
	+                          vate futex word, where all users must use the
	+                          same virtual memory map (like FUTEX_WAIT_PRI‐
	+                          VATE; this most often means they are part of
	+                          the same process).  Private futexes are
	+                          faster than shared ones.
	 
	-     Programs should assign to .uaddr by casting a pointer to
	-     uintptr_t.
	+     .__reserved
	+            Specify as 0.
	 
	 RETURN VALUE
	      Returns an index to an arbitrary entry in waiters corresponding to
	      some woken‐up futex.  This implies no information about other
	      waiters.
	 
	      On error, -1 is returned, and errno is set to indicate the error.
	 
	 ERRORS
	      EFAULT
	@@ -106,33 +152,32 @@ ERRORS
		     ject——that is, the address is not aligned appropriately for
		     the specified FUTEX2_SIZE_*.
	 
	      EINVAL
		     flags was not 0.
	 
	      EINVAL
		     n was not in the range [1, FUTEX_WAITV_MAX].
	 
	      EINVAL
	-            timeout was not NULL and clockid was not a valid clock
	-            (CLOCK_MONOTONIC or CLOCK_REALTIME).
	+            timeout was not NULL and clockid was not a valid clock.
	 
	      EINVAL
		     *timeout is denormal (before epoch or tv_nsec not in the
		     range [0, 999'999'999]).
	 
	      EINVAL
		     Any waiters[].flags field contains an unknown flag.
	 
	      EINVAL
	-            Any waiters[].flags field is missing a FUTEX2_SIZE_* flag
	-            or has a size flag different than FUTEX2_SIZE_U32 set.
	+            Any waiters[].flags field is missing a size flag or has an
	+            unsupported one.
	 
	      EINVAL
		     Any waiters[].__reserved field is not 0.
	 
	      EINVAL
		     Any waiters[].value field has more bits set than permitted
		     than the size flags.
	 
	      EINVAL
		     FUTEX2_NUMA was set in waiters[].flags, and the NUMA word
	@@ -151,25 +196,20 @@ ERRORS
	 
	      EAGAIN or EWOULDBLOCK
		     The value in .uaddr[0] was not equal to the expected value
		     .val at the time of the call.
	 
	      EINTR  The operation was interrupted by a signal (see signal(7)).
	 
	 STANDARDS
	      Linux.
	 
	-NOTES
	-     FUTEX2_SIZE_U8, FUTEX2_SIZE_U16, and FUTEX2_SIZE_U64 where .val
	-     and .uaddr[] are 8, 16, or 64 bits are defined, but not imple‐
	-     mented (EINVAL).
	-
	 HISTORY
	      Linux 5.16.
	 
	 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).  ! indicates the futex that woke up
	      each futex_waitv().


What do you think?  I think the formatted page is more readable.
The source gets more complex, but I could live with that.


Cheers,
Alex


Alejandro Colomar (1):
  man/man2/futex_waitv.2: Move text to a new PARAMETERS section

 man/man2/futex_waitv.2 | 157 ++++++++++++++++++++++++++---------------
 1 file changed, 99 insertions(+), 58 deletions(-)

Range-diff against v0:
-:  ------------ > 1:  22d6ad793c4c man/man2/futex_waitv.2: Move text to a new PARAMETERS section

base-commit: ee4402e21d91c285f5f85071713985f2be7ac412
-- 
2.51.0


^ permalink raw reply	[flat|nested] 53+ messages in thread

* [PATCH v1 1/1] man/man2/futex_waitv.2: Move text to a new PARAMETERS section
  2026-02-18  0:41 ` [PATCH v1 0/1] futex_waitv.2: Move text to a new PARAMETERS section Alejandro Colomar
@ 2026-02-18  0:41   ` Alejandro Colomar
  2026-02-18 20:16   ` [PATCH v1 0/1] futex_waitv.2: " наб
  1 sibling, 0 replies; 53+ messages in thread
From: Alejandro Colomar @ 2026-02-18  0:41 UTC (permalink / raw)
  To: linux-man; +Cc: Alejandro Colomar, Ahelenia Ziemiańska

Cc: Ahelenia Ziemiańska <nabijaczleweli@nabijaczleweli.xyz>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
---
 man/man2/futex_waitv.2 | 157 ++++++++++++++++++++++++++---------------
 1 file changed, 99 insertions(+), 58 deletions(-)

diff --git a/man/man2/futex_waitv.2 b/man/man2/futex_waitv.2
index 7e336fec830d..d91050176809 100644
--- a/man/man2/futex_waitv.2
+++ b/man/man2/futex_waitv.2
@@ -65,7 +65,22 @@ .SH DESCRIPTION
 .IR waiters []. val ,
 the call fails immediately with the error
 .BR EAGAIN .
-.P
+.\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH PARAMETERS
+.SS futex_waitv()
+.TP
+.I waiters
+Futex words to monitor are given by
+.IR "struct futex_waitv" ,
+whose fields are similar to
+.BR FUTEX_WAIT (2const)
+parameters.
+.TP
+.I n
+Specify as
+.IR countof(waiters) .
+.TP
+.I timeout
 If
 .I timeout
 is NULL,
@@ -76,38 +91,42 @@ .SH DESCRIPTION
 .IR clockid .
 This interval will be rounded up to the system clock granularity,
 and is guaranteed not to expire early.
-.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 one of
-.BI FUTEX2_SIZE_ *
-ORed with some of the flags below.
 .TP
-.B FUTEX2_SIZE_U32
+.I clockid
+Supported clocks:
+.RS
+.TP
+.B CLOCK_MONOTONIC
+.TQ
+.B CLOCK_REALTIME
+.RE
+.TP
+.I flags
+Specify as
+.BR 0 .
+.\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SS struct futex_waitv
+.TP
 .I .val
-and
-.I .uaddr[]
-are 32-bit unsigned integers.
+Expected value of
+.IR .uaddr[0] .
 .TP
+.I .uaddr
+.RS
+.TP
+.I .uaddr[0]
+See
+.BR FUTEX_WAIT (2const).
+.TP
+.I .uaddr[1]
+See
 .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] ),
+below.
+This field,
+if it exists,
+must contain a NUMA word,
 which can be either
 .B FUTEX_NO_NODE
-(all bits set)
 or a NUMA node number.
 .IP
 If the NUMA word is
@@ -122,6 +141,50 @@ .SH DESCRIPTION
 have
 .B FUTEX2_NUMA
 set.)
+.RE
+.IP
+Programs should assign to
+.I .uaddr
+by casting a pointer to
+.IR uintptr_t .
+.RE
+.TP
+.I .flags
+It must contain
+exactly one size flag
+ORed with
+zero or more of the other flags.
+.RS
+.TP
+Size flags
+.RS
+.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 ).
+.RE
+.TP
+Other flags
+.RS
+.TP
+.B FUTEX2_NUMA
+The futex word is followed by the NUMA word,
+of the same size
+.RI ( .uaddr
+points to
+.IR uint N _t[2]
+rather than
+.IR uint N _t ).
 .IP
 Futexes are placed on the NUMA node given by the NUMA word.
 Futexes without this flag are placed on a random node.
@@ -142,14 +205,13 @@ .SH DESCRIPTION
 .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 .
-.\"
+.RE
+.RE
+.TP
+.I .__reserved
+Specify as
+.BR 0 .
 .\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
-.\"
 .SH RETURN VALUE
 Returns an index to an arbitrary entry in
 .I waiters
@@ -161,9 +223,7 @@ .SH RETURN VALUE
 and
 .I errno
 is set to indicate the error.
-.\"
 .\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
-.\"
 .SH ERRORS
 .TP
 .B EFAULT
@@ -200,10 +260,7 @@ .SH ERRORS
 .I timeout
 was not NULL and
 .I clockid
-was not a valid clock
-.RB ( CLOCK_MONOTONIC
-or
-.BR CLOCK_REALTIME ).
+was not a valid clock.
 .TP
 .B EINVAL
 .I *timeout
@@ -221,11 +278,8 @@ .SH ERRORS
 .B EINVAL
 Any
 .IR waiters []. flags
-field is missing a
-.BI FUTEX2_SIZE_ *
-flag or has a size flag different than
-.B FUTEX2_SIZE_U32
-set.
+field is missing a size flag
+or has an unsupported one.
 .TP
 .B EINVAL
 Any
@@ -269,22 +323,9 @@ .SH ERRORS
 The
 operation was interrupted by a signal (see
 .BR signal (7)).
-.\"
 .\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
-.\"
 .SH STANDARDS
 Linux.
-.SH NOTES
-.BR FUTEX2_SIZE_U8 ,
-.BR FUTEX2_SIZE_U16 ,
-and
-.B FUTEX2_SIZE_U64
-where
-.I .val
-and
-.I .uaddr[]
-are 8, 16, or 64 bits are defined, but not implemented
-.RB ( EINVAL ).
 .SH HISTORY
 .\" commit bf69bad38cf63d980e8a603f8d1bd1f85b5ed3d9
 .\" Author: André Almeida <andrealmeid@igalia.com>
-- 
2.51.0


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* Re: [PATCH v1 0/1] futex_waitv.2: Move text to a new PARAMETERS section
  2026-02-18  0:41 ` [PATCH v1 0/1] futex_waitv.2: Move text to a new PARAMETERS section Alejandro Colomar
  2026-02-18  0:41   ` [PATCH v1 1/1] man/man2/futex_waitv.2: " Alejandro Colomar
@ 2026-02-18 20:16   ` наб
  2026-02-18 20:26     ` Alejandro Colomar
  1 sibling, 1 reply; 53+ messages in thread
From: наб @ 2026-02-18 20:16 UTC (permalink / raw)
  To: Alejandro Colomar; +Cc: linux-man

[-- Attachment #1: Type: text/plain, Size: 9302 bytes --]

On Wed, Feb 18, 2026 at 01:41:38AM +0100, Alejandro Colomar wrote:
> Please let me know what you think of this patch?
> Here's how the patch changes the page:
> 
> 	$ MANWIDTH=72 diffman-git -U10 HEAD
> 	--- HEAD^:man/man2/futex_waitv.2
> 	+++ HEAD:man/man2/futex_waitv.2
> 	@@ -37,57 +37,103 @@ DESCRIPTION
> 	      ers[].uaddr[0] still contain respective expected values wait‐
> 	      ers[].val, and if so, sleeps waiting for a FUTEX_WAKE(2const) op‐
> 	      eration on any of the futex words, and returns the index of a
> 	      waiter whose futex was woken.
> 	 
> 	      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 waiters[].val, the call fails immediately with the er‐
> 	      ror EAGAIN.
> 	 
> 	-     If timeout is NULL, the call blocks indefinitely.  Otherwise,
> 	-     *timeout specifies a deadline measured against clock clockid.
> 	-     This interval will be rounded up to the system clock granularity,
> 	-     and is guaranteed not to expire early.
> 	-
> 	-     Futex words to monitor are given by struct futex_waitv, whose
> 	-     fields are analogous to FUTEX_WAIT(2const) parameters, except
> 	-     .__reserved must be 0 and .flags must contain one of FUTEX2_SIZE_*
> 	-     ORed with some of the flags below.
> 	-
> 	-     FUTEX2_SIZE_U32
> 	-            .val and .uaddr[] are 32‐bit unsigned integers.
> 	-
> 	-     FUTEX2_NUMA
> 	-            The futex word is followed by another word of the same size
> 	-            (.uaddr points to uintN_t[2] rather than uintN_t.  The word
> 	-            is given by .uaddr[1]), which can be either FUTEX_NO_NODE
> 	-            (all bits set) or a NUMA node number.
> 	-
> 	-            If the NUMA word is FUTEX_NO_NODE, the node number of the
> 	-            processor the syscall executes on is written to it.  (Ex‐
> 	-            cept that in an EINVAL or EFAULT condition, this happens to
> 	-            all waiters whose .flags have FUTEX2_NUMA set.)
> 	-
> 	-            Futexes are placed on the NUMA node given by the NUMA word.
> 	-            Futexes without this flag are placed on a random node.
> 	-
> 	-     FUTEX2_PRIVATE
> 	-            By default, the futex is shared (like 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 FUTEX_WAIT_PRIVATE; this most of‐
> 	-            ten means they are part of the same process).  Private fu‐
> 	-            texes are faster than shared ones.
> 	+PARAMETERS
> 	+   futex_waitv()
> 	+     waiters
> 	+            Futex words to monitor are given by struct futex_waitv,
> 	+            whose fields are similar to FUTEX_WAIT(2const) parameters.
> 	+
> 	+     n      Specify as countof(waiters).
> 	+
> 	+     timeout
> 	+            If timeout is NULL, the call blocks indefinitely.  Other‐
> 	+            wise, *timeout specifies a deadline measured against clock
> 	+            clockid.  This interval will be rounded up to the system
> 	+            clock granularity, and is guaranteed not to expire early.
> 	+
> 	+     clockid
> 	+            Supported clocks:
> 	+
> 	+            CLOCK_MONOTONIC
> 	+            CLOCK_REALTIME
> 	+
> 	+     flags  Specify as 0.
> 	+
> 	+   struct futex_waitv
> 	+     .val   Expected value of .uaddr[0].
> 	+
> 	+     .uaddr
> 	+
> 	+            .uaddr[0]
> 	+                   See FUTEX_WAIT(2const).
> 	+
> 	+            .uaddr[1]
> 	+                   See FUTEX2_NUMA below.  This field, if it exists,
> 	+                   must contain a NUMA word, which can be either FU‐
> 	+                   TEX_NO_NODE or a NUMA node number.
> 	+
> 	+                   If the NUMA word is FUTEX_NO_NODE, the node number
> 	+                   of the processor the syscall executes on is written
> 	+                   to it.  (Except that in an EINVAL or EFAULT condi‐
> 	+                   tion, this happens to all waiters whose .flags have
> 	+                   FUTEX2_NUMA set.)
> 	+
> 	+            Programs should assign to .uaddr by casting a pointer to
> 	+            uintptr_t.
> 	+
> 	+     .flags
> 	+            It must contain exactly one size flag ORed with zero or
> 	+            more of the other flags.
> 	+
> 	+            Size flags
> 	+
> 	+                   FUTEX2_SIZE_U32
> 	+                          .val and .uaddr[] are 32‐bit unsigned inte‐
> 	+                          gers.
> 	+
> 	+                   FUTEX2_SIZE_U8
> 	+                   FUTEX2_SIZE_U16
> 	+                   FUTEX2_SIZE_U64
> 	+                          These are defined, but not supported (EIN‐
> 	+                          VAL).
> 	+
> 	+            Other flags
> 	+
> 	+                   FUTEX2_NUMA
> 	+                          The futex word is followed by the NUMA word,
> 	+                          of the same size (.uaddr points to uintN_t[2]
> 	+                          rather than uintN_t).
> 	+
> 	+                          Futexes are placed on the NUMA node given by
> 	+                          the NUMA word.  Futexes without this flag are
> 	+                          placed on a random node.
> 	+
> 	+                   FUTEX2_PRIVATE
> 	+                          By default, the futex is shared (like FU‐
> 	+                          TEX_WAIT(2const)), and can be accessed by
> 	+                          multiple processes; this flag waits on a pri‐
> 	+                          vate futex word, where all users must use the
> 	+                          same virtual memory map (like FUTEX_WAIT_PRI‐
> 	+                          VATE; this most often means they are part of
> 	+                          the same process).  Private futexes are
> 	+                          faster than shared ones.
> 	 
> 	-     Programs should assign to .uaddr by casting a pointer to
> 	-     uintptr_t.
> 	+     .__reserved
> 	+            Specify as 0.
> 	 
> 	 RETURN VALUE
> 	      Returns an index to an arbitrary entry in waiters corresponding to
> 	      some woken‐up futex.  This implies no information about other
> 	      waiters.
> 	 
> 	      On error, -1 is returned, and errno is set to indicate the error.
> 	 
> 	 ERRORS
> 	      EFAULT
> 	@@ -106,33 +152,32 @@ ERRORS
> 		     ject——that is, the address is not aligned appropriately for
> 		     the specified FUTEX2_SIZE_*.
> 	 
> 	      EINVAL
> 		     flags was not 0.
> 	 
> 	      EINVAL
> 		     n was not in the range [1, FUTEX_WAITV_MAX].
> 	 
> 	      EINVAL
> 	-            timeout was not NULL and clockid was not a valid clock
> 	-            (CLOCK_MONOTONIC or CLOCK_REALTIME).
> 	+            timeout was not NULL and clockid was not a valid clock.
> 	 
> 	      EINVAL
> 		     *timeout is denormal (before epoch or tv_nsec not in the
> 		     range [0, 999'999'999]).
> 	 
> 	      EINVAL
> 		     Any waiters[].flags field contains an unknown flag.
> 	 
> 	      EINVAL
> 	-            Any waiters[].flags field is missing a FUTEX2_SIZE_* flag
> 	-            or has a size flag different than FUTEX2_SIZE_U32 set.
> 	+            Any waiters[].flags field is missing a size flag or has an
> 	+            unsupported one.
> 	 
> 	      EINVAL
> 		     Any waiters[].__reserved field is not 0.
> 	 
> 	      EINVAL
> 		     Any waiters[].value field has more bits set than permitted
> 		     than the size flags.
> 	 
> 	      EINVAL
> 		     FUTEX2_NUMA was set in waiters[].flags, and the NUMA word
> 	@@ -151,25 +196,20 @@ ERRORS
> 	 
> 	      EAGAIN or EWOULDBLOCK
> 		     The value in .uaddr[0] was not equal to the expected value
> 		     .val at the time of the call.
> 	 
> 	      EINTR  The operation was interrupted by a signal (see signal(7)).
> 	 
> 	 STANDARDS
> 	      Linux.
> 	 
> 	-NOTES
> 	-     FUTEX2_SIZE_U8, FUTEX2_SIZE_U16, and FUTEX2_SIZE_U64 where .val
> 	-     and .uaddr[] are 8, 16, or 64 bits are defined, but not imple‐
> 	-     mented (EINVAL).
> 	-
> 	 HISTORY
> 	      Linux 5.16.
> 	 
> 	 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).  ! indicates the futex that woke up
> 	      each futex_waitv().
> 
> 
> What do you think?  I think the formatted page is more readable.
I really hate it.

> The source gets more complex, but I could live with that.
> 
> 
> Cheers,
> Alex
> 
> 
> Alejandro Colomar (1):
>   man/man2/futex_waitv.2: Move text to a new PARAMETERS section
> 
>  man/man2/futex_waitv.2 | 157 ++++++++++++++++++++++++++---------------
>  1 file changed, 99 insertions(+), 58 deletions(-)
> 
> Range-diff against v0:
> -:  ------------ > 1:  22d6ad793c4c man/man2/futex_waitv.2: Move text to a new PARAMETERS section
> 
> base-commit: ee4402e21d91c285f5f85071713985f2be7ac412
> -- 
> 2.51.0
> 

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v1 0/1] futex_waitv.2: Move text to a new PARAMETERS section
  2026-02-18 20:16   ` [PATCH v1 0/1] futex_waitv.2: " наб
@ 2026-02-18 20:26     ` Alejandro Colomar
  2026-02-18 20:30       ` наб
  0 siblings, 1 reply; 53+ messages in thread
From: Alejandro Colomar @ 2026-02-18 20:26 UTC (permalink / raw)
  To: наб; +Cc: linux-man

[-- Attachment #1: Type: text/plain, Size: 455 bytes --]

Hi,

On 2026-02-18T21:16:06+0100, наб wrote:
> On Wed, Feb 18, 2026 at 01:41:38AM +0100, Alejandro Colomar wrote:
> > Please let me know what you think of this patch?
> > Here's how the patch changes the page:
> > 
[...]
> > What do you think?  I think the formatted page is more readable.
> I really hate it.

Okay.  Do you like any part, or is it all hateful?  :-)


Have a lovely day!
Alex

-- 
<https://www.alejandro-colomar.es>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v1 0/1] futex_waitv.2: Move text to a new PARAMETERS section
  2026-02-18 20:26     ` Alejandro Colomar
@ 2026-02-18 20:30       ` наб
  2026-02-18 20:33         ` Alejandro Colomar
  2026-02-18 20:40         ` G. Branden Robinson
  0 siblings, 2 replies; 53+ messages in thread
From: наб @ 2026-02-18 20:30 UTC (permalink / raw)
  To: Alejandro Colomar; +Cc: linux-man

[-- Attachment #1: Type: text/plain, Size: 527 bytes --]

On Wed, Feb 18, 2026 at 09:26:38PM +0100, Alejandro Colomar wrote:
> On 2026-02-18T21:16:06+0100, наб wrote:
> > On Wed, Feb 18, 2026 at 01:41:38AM +0100, Alejandro Colomar wrote:
> > > Please let me know what you think of this patch?
> > > Here's how the patch changes the page:
> [...]
> > > What do you think?  I think the formatted page is more readable.
> > I really hate it.
> Okay.  Do you like any part, or is it all hateful?  :-)
The format annoys me, the layout infuriates me, I find the tone insulting.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v1 0/1] futex_waitv.2: Move text to a new PARAMETERS section
  2026-02-18 20:30       ` наб
@ 2026-02-18 20:33         ` Alejandro Colomar
  2026-02-18 20:40         ` G. Branden Robinson
  1 sibling, 0 replies; 53+ messages in thread
From: Alejandro Colomar @ 2026-02-18 20:33 UTC (permalink / raw)
  To: наб; +Cc: linux-man

[-- Attachment #1: Type: text/plain, Size: 658 bytes --]

On 2026-02-18T21:30:53+0100, наб wrote:
> On Wed, Feb 18, 2026 at 09:26:38PM +0100, Alejandro Colomar wrote:
> > On 2026-02-18T21:16:06+0100, наб wrote:
> > > On Wed, Feb 18, 2026 at 01:41:38AM +0100, Alejandro Colomar wrote:
> > > > Please let me know what you think of this patch?
> > > > Here's how the patch changes the page:
> > [...]
> > > > What do you think?  I think the formatted page is more readable.
> > > I really hate it.
> > Okay.  Do you like any part, or is it all hateful?  :-)
> The format annoys me, the layout infuriates me,

Okay.

> I find the tone insulting.

Wut?


-- 
<https://www.alejandro-colomar.es>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v1 0/1] futex_waitv.2: Move text to a new PARAMETERS section
  2026-02-18 20:30       ` наб
  2026-02-18 20:33         ` Alejandro Colomar
@ 2026-02-18 20:40         ` G. Branden Robinson
  2026-02-18 21:28           ` Alejandro Colomar
  1 sibling, 1 reply; 53+ messages in thread
From: G. Branden Robinson @ 2026-02-18 20:40 UTC (permalink / raw)
  To: linux-man

[-- Attachment #1: Type: text/plain, Size: 1011 bytes --]

At 2026-02-18T21:30:53+0100, наб wrote:
> On Wed, Feb 18, 2026 at 09:26:38PM +0100, Alejandro Colomar wrote:
> > On 2026-02-18T21:16:06+0100, наб wrote:
> > > On Wed, Feb 18, 2026 at 01:41:38AM +0100, Alejandro Colomar wrote:
> > > > Please let me know what you think of this patch?
> > > > Here's how the patch changes the page:
> > [...]
> > > > What do you think?  I think the formatted page is more readable.
> > > I really hate it.
> > Okay.  Do you like any part, or is it all hateful?  :-)
> The format annoys me, the layout infuriates me, I find the tone
> insulting.

When dealing with formally complex (meaning: decomposable) proposals,
rapprochement is difficult with a binary oracle.

Alex,

The onus might be on you to correspondingly decompose наб's black box.

Some obvious candidates include the two hunks of the end of the
diff of the formatted document that change only a few words, and the
existence of a "PARAMETERS" section of the page.

Regards,
Branden

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v1 0/1] futex_waitv.2: Move text to a new PARAMETERS section
  2026-02-18 20:40         ` G. Branden Robinson
@ 2026-02-18 21:28           ` Alejandro Colomar
  2026-02-18 22:04             ` Alejandro Colomar
  0 siblings, 1 reply; 53+ messages in thread
From: Alejandro Colomar @ 2026-02-18 21:28 UTC (permalink / raw)
  To: G. Branden Robinson; +Cc: linux-man, наб

[-- Attachment #1: Type: text/plain, Size: 2565 bytes --]

Hi Branden, наб,

On 2026-02-18T14:40:12-0600, G. Branden Robinson wrote:
> At 2026-02-18T21:30:53+0100, наб wrote:
> > On Wed, Feb 18, 2026 at 09:26:38PM +0100, Alejandro Colomar wrote:
> > > On 2026-02-18T21:16:06+0100, наб wrote:
> > > > On Wed, Feb 18, 2026 at 01:41:38AM +0100, Alejandro Colomar wrote:
> > > > > Please let me know what you think of this patch?
> > > > > Here's how the patch changes the page:
> > > [...]
> > > > > What do you think?  I think the formatted page is more readable.
> > > > I really hate it.
> > > Okay.  Do you like any part, or is it all hateful?  :-)
> > The format annoys me, the layout infuriates me, I find the tone
> > insulting.
> 
> When dealing with formally complex (meaning: decomposable) proposals,
> rapprochement is difficult with a binary oracle.
> 
> Alex,
> 
> The onus might be on you to correspondingly decompose наб's black box.
> 
> Some obvious candidates include the two hunks of the end of the
> diff of the formatted document that change only a few words, and the
> existence of a "PARAMETERS" section of the page.

Hmmm, I've now pushed a much smaller change:

	$ MANWIDTH=64 diffman-git HEAD
	--- HEAD^:man/man2/futex_waitv.2
	+++ HEAD:man/man2/futex_waitv.2
	@@ -54,11 +54,16 @@ DESCRIPTION
	      Futex words to monitor are given by struct futex_waitv,
	      whose fields are analogous to FUTEX_WAIT(2const) parame‐
	      ters, except .__reserved must be 0 and .flags must contain
	-     one of FUTEX2_SIZE_* ORed with some of the flags below.
	+     exactly one size flag, ORed with some other flags.
	 
	      FUTEX2_SIZE_U32
		     .val and .uaddr[] are 32‐bit unsigned integers.
	 
	+     FUTEX2_SIZE_U8
	+     FUTEX2_SIZE_U16
	+     FUTEX2_SIZE_U64
	+            These are defined, but not supported (EINVAL).
	+
	      FUTEX2_NUMA
		     The futex word is followed by another word of the
		     same size (.uaddr points to uintN_t[2] rather than
	@@ -132,9 +137,8 @@ ERRORS
		     Any waiters[].flags field contains an unknown flag.
	 
	      EINVAL
	-            Any waiters[].flags field is missing a FU‐
	-            TEX2_SIZE_* flag or has a size flag different than
	-            FUTEX2_SIZE_U32 set.
	+            Any waiters[].flags field does not contain exactly
	+            one size flag, or it contains an unsupported one.
	 
	      EINVAL
		     Any waiters[].__reserved field is not 0.

I think this one should be uncontroversial.


Cheers,
Alex

-- 
<https://www.alejandro-colomar.es>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v1 0/1] futex_waitv.2: Move text to a new PARAMETERS section
  2026-02-18 21:28           ` Alejandro Colomar
@ 2026-02-18 22:04             ` Alejandro Colomar
  0 siblings, 0 replies; 53+ messages in thread
From: Alejandro Colomar @ 2026-02-18 22:04 UTC (permalink / raw)
  To: G. Branden Robinson; +Cc: linux-man, наб

[-- Attachment #1: Type: text/plain, Size: 1017 bytes --]

On 2026-02-18T22:28:16+0100, Alejandro Colomar wrote:
> I think this one should be uncontroversial.

And here's another one I'll push in a moment:

	$ MANWIDTH=64 diffman-git 
	--- HEAD:man/man2/futex_waitv.2
	+++ ./man/man2/futex_waitv.2
	@@ -51,6 +51,11 @@ DESCRIPTION
	      clockid.  This interval will be rounded up to the system
	      clock granularity, and is guaranteed not to expire early.
	 
	+     The following clocks are supported:
	+
	+     CLOCK_MONOTONIC
	+     CLOCK_REALTIME
	+
	      Futex words to monitor are given by struct futex_waitv,
	      whose fields are analogous to FUTEX_WAIT(2const) parame‐
	      ters, except .__reserved must be 0 and .flags must contain
	@@ -127,7 +132,7 @@ ERRORS
	 
	      EINVAL
		     timeout was not NULL and clockid was not a valid
	-            clock (CLOCK_MONOTONIC or CLOCK_REALTIME).
	+            clock.
	 
	      EINVAL
		     *timeout is denormal (before epoch or tv_nsec not

-- 
<https://www.alejandro-colomar.es>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 53+ messages in thread

end of thread, other threads:[~2026-02-18 22:04 UTC | newest]

Thread overview: 53+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-07 12:49 [PATCH] futex_waitv.2: new page наб
2026-02-07 18:57 ` Alejandro Colomar
2026-02-07 19:16   ` наб
2026-02-07 21:50     ` Alejandro Colomar
2026-02-07 22:00 ` [PATCH v2] " наб
2026-02-09 22:35   ` Alejandro Colomar
2026-02-10 14:17     ` наб
2026-02-10 14:30       ` Alejandro Colomar
2026-02-10 15:54         ` Kristoffer Haugsbakk
2026-02-10 18:39           ` Alejandro Colomar
2026-02-11  7:35           ` Jeff King
2026-02-11  8:15             ` Kristoffer Haugsbakk
2026-02-11 15:43             ` Junio C Hamano
2026-02-10 16:54         ` Junio C Hamano
2026-02-10 17:11           ` Kristoffer Haugsbakk
2026-02-10 18:44           ` Alejandro Colomar
2026-02-10 20:05   ` Alejandro Colomar
2026-02-10 20:32     ` [PATCH v3] " наб
2026-02-10 21:11       ` Alejandro Colomar
2026-02-11  4:00         ` [PATCH v4] " наб
2026-02-11 13:23           ` Alejandro Colomar
2026-02-11 13:51             ` [PATCH v5] " наб
2026-02-11 14:15               ` [PATCH v6] " наб
2026-02-11 14:31                 ` Alejandro Colomar
2026-02-11 14:44                   ` [PATCH v7] " наб
2026-02-11 14:55                     ` Alejandro Colomar
2026-02-11 14:59                       ` наб
2026-02-11 15:13                         ` Alejandro Colomar
2026-02-14 17:32                     ` Alejandro Colomar
2026-02-14 19:30                       ` [PATCH v8] " наб
2026-02-14 20:03                         ` Alejandro Colomar
2026-02-14 20:48                           ` [PATCH v9] " наб
2026-02-15 18:18                             ` Alejandro Colomar
2026-02-15 19:00                               ` [PATCH v10] " наб
2026-02-16  0:32                                 ` Alejandro Colomar
2026-02-16 14:20                                   ` [PATCH v11] " наб
2026-02-16 14:50                                     ` Alejandro Colomar
2026-02-16 20:43                                       ` [PATCH v12] " наб
2026-02-17 13:07                                         ` Alejandro Colomar
2026-02-17 14:31                                           ` [PATCH v13] " наб
2026-02-17 15:46                                             ` Alejandro Colomar
2026-02-17 16:17                                               ` наб
2026-02-18  0:31                                                 ` Alejandro Colomar
2026-02-11 14:28               ` [PATCH v5] " Alejandro Colomar
2026-02-18  0:41 ` [PATCH v1 0/1] futex_waitv.2: Move text to a new PARAMETERS section Alejandro Colomar
2026-02-18  0:41   ` [PATCH v1 1/1] man/man2/futex_waitv.2: " Alejandro Colomar
2026-02-18 20:16   ` [PATCH v1 0/1] futex_waitv.2: " наб
2026-02-18 20:26     ` Alejandro Colomar
2026-02-18 20:30       ` наб
2026-02-18 20:33         ` Alejandro Colomar
2026-02-18 20:40         ` G. Branden Robinson
2026-02-18 21:28           ` Alejandro Colomar
2026-02-18 22:04             ` Alejandro Colomar

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox