futex-waitv.dhover×167all
#!/usr/bin/env dub
/+ dub.sdl:
    name "io_uring_futex_waitv"
    dependency "during" version="~>0.5.0"
    platforms "linux"
    targetPath "build"
+/
/**
 * `io_uring` — vectored futex wait (`IORING_OP_FUTEX_WAITV`, Linux 6.7).
 *
 * `FUTEX_WAITV` arms a wait on a *vector* of futexes at once and completes as
 * soon as *any one* of them is woken — the async, batched counterpart of the
 * legacy `futex_waitv(2)` syscall. This is the building block a runtime uses to
 * block a task on several wakeup sources (e.g. several condition words) without
 * burning a thread per source.
 *
 * This example builds an array of two `futex_waitv` entries over two distinct
 * 32-bit words, submits a single `prepFutexWaitv(&v[0], 2, 0)`, and spins up a
 * helper pthread that wakes the *second* word via the legacy `futex(2)`
 * `FUTEX_WAKE_PRIVATE` syscall. The io_uring FUTEX2 waiter and the legacy futex
 * share the same kernel hash bucket for a given `uaddr`, so a legacy wake on a
 * matching, `FUTEX2_PRIVATE`-armed address wakes the io_uring waiter. We assert
 * the completion `res >= 0` (a wake / the index of the woken futex), not an error.
 *
 * The helper retries every 5 ms (bounded) to defeat the inherent race between
 * submission and the kernel actually parking the waiter; the main thread also
 * bounds its `wait` with a linked `TIMEOUT` so a misbehaving kernel can't hang.
 *
 * Companion to the io_uring chronology:
 * see docs/research/async-io/io-uring/timeline.md
 *   § "6.7 — Futex, waitid, read-multishot (January 2024)".
 *
 * Run with: `dub run --single futex-waitv.d`
 *
 * Portability: if the running kernel has no `io_uring` (too old / blocked by a
 * seccomp or container policy), or lacks `FUTEX_WAITV` (kernel < 6.7), the
 * program prints a `SKIP:` line and exits 0 so it stays green in CI regardless
 * of the host kernel. This box runs 6.18, where the feature is present.
 */
module 
(module) io_uring_futex_waitv

io_uring — vectored futex wait (IORING_OP_FUTEX_WAITV, Linux 6.7).

FUTEX_WAITV arms a wait on a vector of futexes at once and completes as soon as any one of them is woken — the async, batched counterpart of the legacy futex_waitv(2) syscall. This is the building block a runtime uses to block a task on several wakeup sources (e.g. several condition words) without burning a thread per source.

This example builds an array of two futex_waitv entries over two distinct 32-bit words, submits a single prepFutexWaitv(&v[0], 2, 0), and spins up a helper pthread that wakes the second word via the legacy futex(2) FUTEX_WAKE_PRIVATE syscall. The io_uring FUTEX2 waiter and the legacy futex share the same kernel hash bucket for a given uaddr, so a legacy wake on a matching, FUTEX2_PRIVATE-armed address wakes the io_uring waiter. We assert the completion res >= 0 (a wake / the index of the woken futex), not an error.

The helper retries every 5 ms (bounded) to defeat the inherent race between submission and the kernel actually parking the waiter; the main thread also bounds its wait with a linked TIMEOUT so a misbehaving kernel can't hang.

Companion to the io_uring chronology: see docs/research/async-io/io-uring/timeline.md § "6.7 — Futex, waitid, read-multishot (January 2024)".

Run with: dub run --single futex-waitv.d

Portability

if the running kernel has no io_uring (too old / blocked by a seccomp or container policy), or lacks FUTEX_WAITV (kernel < 6.7), the program prints a SKIP: line and exits 0 so it stays green in CI regardless of the host kernel. This box runs 6.18, where the feature is present.

io_uring_futex_waitv
;
import
(module) during

Simple idiomatic dlang wrapper around linux io_uring (see: https://kernel.dk/io_uring.pdf) asynchronous API.

during
;
import
(package) std
std
.
(module) std.stdio
Category Symbols
File handles _popen File isFileHandle openNetwork stderr stdin stdout
Reading chunks lines readf readfln readln
Writing toFile write writef writefln writeln
Misc KeepTerminator LockType StdioException

Standard I/O functions that extend core.stdc.stdio. core.stdc.stdio is publically imported when importing std.stdio.

There are three layers of I/O:

  1. The lowest layer is the operating system layer. The two main schemes are Windows and Posix.

  2. C's stdio.h which unifies the two operating system schemes.

  3. std.stdio, this module, unifies the various stdio.h implementations into a high level package for D programs.

Source

std/stdio.d

@copyrightCopyright The D Language Foundation 2007-.@licenseBoost License 1.0.@authorsWalter Bright, Andrei Alexandrescu, Alex Rønne Petersen
stdio
:
(alias template) io_uring_futex_waitv.writefln = std.stdio.writefln(alias fmt, A...)(A args) if (isSomeString!(typeof(fmt)))

Equivalent to writef(fmt, args, '\n').

writefln
, stderr;
import
(package) core
core
.
(module) core.atomic

The atomic module provides basic support for lock-free concurrent programming.

Use the -preview=nosharedaccess compiler flag to detect unsafe individual read or write operations on shared data.

Source

core/atomic.d

Examples

int y = 2;
shared int x = y; // OK

//x++; // read modify write error
x.atomicOp!"+="(1); // OK
//y = x; // read error with preview flag
y = x.atomicLoad(); // OK
assert(y == 3);
//x = 5; // write error with preview flag
x.atomicStore(5); // OK
assert(x.atomicLoad() == 5);
@copyrightCopyright Sean Kelly 2005 - 2016.@licenseBoost License 1.0@authorsSean Kelly, Alex Rønne Petersen, Manu Evans
atomic
:
(alias template) io_uring_futex_waitv.atomicLoad = core.atomic.atomicLoad(MemoryOrder ms = MemoryOrder.seq, T)(auto ref return scope const T val) if (!is(T == shared(U), U) && !is(T == shared(inout(U)), U) && !is(T == shared(const(U)), U))

Loads 'val' from memory and returns it. The memory barrier specified by 'ms' is applied to the operation, which is fully sequenced by default. Valid memory orders are MemoryOrder.raw, MemoryOrder.acq, and MemoryOrder.seq.

@paramval The target variable.@returnsThe value of 'val'.
atomicLoad
,
(alias template) io_uring_futex_waitv.atomicStore = core.atomic.atomicStore(MemoryOrder ms = MemoryOrder.seq, T, V)(ref T val, V newval) if (!is(T == shared) && !is(V == shared))

Writes 'newval' into 'val'. The memory barrier specified by 'ms' is applied to the operation, which is fully sequenced by default. Valid memory orders are MemoryOrder.raw, MemoryOrder.rel, and MemoryOrder.seq.

@paramval The target variable.@paramnewval The value to store.
atomicStore
,
(enum) core.atomic.MemoryOrder

Specifies the memory ordering semantics of an atomic operation.

@see
MemoryOrder
;
import
(package) core
core
.
(package) core.sys
sys
.
(package) core.sys.linux
linux
.
(module) core.sys.linux.errno

D header file for GNU/Linux

glibc stdlib/errno.h

errno
:
(alias constant) io_uring_futex_waitv.EINVAL = int core.stdc.errno.EINVAL = 22
EINVAL
,
(alias constant) io_uring_futex_waitv.EOPNOTSUPP = int core.stdc.errno.EOPNOTSUPP = 95
EOPNOTSUPP
,
(alias constant) io_uring_futex_waitv.ENOSYS = int core.stdc.errno.ENOSYS = 38
ENOSYS
;
import
(package) core
core
.
(package) core.sys
sys
.
(package) core.sys.posix
posix
.
(module) core.sys.posix.pthread

D header file for POSIX.

@copyrightCopyright Sean Kelly 2005 - 2009.@licenseBoost License 1.0.@authorsSean Kelly, Alex Rønne Petersen@standardsThe Open Group Base Specifications Issue 6, IEEE Std 1003.1, 2004 Edition
pthread
;
import
(package) core
core
.
(package) core.sys
sys
.
(package) core.sys.posix
posix
.
(module) core.sys.posix.unistd

D header file for POSIX.

@copyrightCopyright Sean Kelly 2005 - 2009.@licenseBoost License 1.0.@authorsSean Kelly@standardsThe Open Group Base Specifications Issue 8, IEEE Std 1003.1, 2024 Edition
unistd
:
(alias) io_uring_futex_waitv.usleep = int core.sys.posix.unistd.usleep(uint) nothrow @nogc @trusted
usleep
;
// `SYS_futex` (the legacy futex(2) syscall) is universally available on Linux. // We use it from the helper thread to issue a `FUTEX_WAKE_PRIVATE`. version (
X86_64
X86_64
) private enum
(constant) int io_uring_futex_waitv.SYS_futex = 202
SYS_futex
= 202;
else version (X86) private enum SYS_futex = 240; else version (AArch64) private enum SYS_futex = 98; else static assert(0, "Unsupported platform for SYS_futex constant"); private enum
(constant) int io_uring_futex_waitv.FUTEX_WAKE = 1
FUTEX_WAKE
= 1;
private enum
(constant) int io_uring_futex_waitv.FUTEX_PRIVATE_FLAG = 128
FUTEX_PRIVATE_FLAG
= 128;
private enum
(constant) int io_uring_futex_waitv.FUTEX_WAKE_PRIVATE = 129
FUTEX_WAKE_PRIVATE
=
(constant) int io_uring_futex_waitv.FUTEX_WAKE = 1
FUTEX_WAKE
|
(constant) int io_uring_futex_waitv.FUTEX_PRIVATE_FLAG = 128
FUTEX_PRIVATE_FLAG
;
private extern (C) int
int io_uring_futex_waitv.syscall(int sysno, ...) nothrow @nogc @system
syscall
(int
(parameter) int sysno
sysno
, ...) nothrow @nogc @system;
// Shared state between the main thread and the wake helper. private struct
(struct) io_uring_futex_waitv.WakeCtx
WakeCtx
{ uint*
(field) uint* io_uring_futex_waitv.WakeCtx.word
word
; // address of the futex word the helper will wake
shared int
(field) shared(int) io_uring_futex_waitv.WakeCtx.stop
stop
; // set by main once the CQE has arrived
int
(field) int io_uring_futex_waitv.WakeCtx.attempts
attempts
; // number of wake attempts the helper made
} // Helper thread: repeatedly `FUTEX_WAKE_PRIVATE`s `ctx.word` until the main // thread sets `stop`. Bounded to ~1s of attempts so it can never hang the run. private extern (C) void*
void* io_uring_futex_waitv.wakeWorker(void* arg) nothrow @nogc @system
wakeWorker
(void*
(parameter) void* arg
arg
) @system nothrow @nogc
{ auto
(local variable) io_uring_futex_waitv.WakeCtx* ctx
ctx
= cast(
(struct) io_uring_futex_waitv.WakeCtx
WakeCtx
*)
(parameter) void* arg
arg
;
foreach (
(local variable) int i
i
; 0 .. 200) // up to ~1s (200 * 5ms)
{ // Re-check before sleeping so we exit promptly once main signals stop. if (
int core.atomic.atomicLoad!(MemoryOrder.acq, int)(ref return scope shared(const(int)) val) pure nothrow @nogc @trusted

Loads 'val' from memory and returns it. The memory barrier specified by 'ms' is applied to the operation, which is fully sequenced by default. Valid memory orders are MemoryOrder.raw, MemoryOrder.acq, and MemoryOrder.seq.

@paramval The target variable.@returnsThe value of 'val'.
atomicLoad
!(
(enum) core.atomic.MemoryOrder

Specifies the memory ordering semantics of an atomic operation.

@see
MemoryOrder
.
(enum value) core.atomic.MemoryOrder.acq = 2

Hoist-load + hoist-store barrier. Corresponds to LLVM AtomicOrdering.Acquire and C++11/C11 memory_order_acquire.

acq
)(
(local variable) io_uring_futex_waitv.WakeCtx* ctx
ctx
.
(field) shared(int) io_uring_futex_waitv.WakeCtx.stop
stop
)) break;
int core.sys.posix.unistd.usleep(uint) nothrow @nogc @trusted
usleep
(5_000); // 5ms
(local variable) io_uring_futex_waitv.WakeCtx* ctx
ctx
.
(field) int io_uring_futex_waitv.WakeCtx.attempts
attempts
=
(local variable) int i
i
+ 1;
// arg2 = val = 1 => wake at most one waiter on this address.
int io_uring_futex_waitv.syscall(int sysno, ...) nothrow @nogc @system
syscall
(
(constant) int io_uring_futex_waitv.SYS_futex = 202
SYS_futex
, cast(void*)
(local variable) io_uring_futex_waitv.WakeCtx* ctx
ctx
.
(field) uint* io_uring_futex_waitv.WakeCtx.word
word
,
(constant) int io_uring_futex_waitv.FUTEX_WAKE_PRIVATE = 129
FUTEX_WAKE_PRIVATE
, 1, null, null, 0);
} return null; } int
int D main()
main
()
{
(struct) during.Uring

Main entry point to work with io_uring.

It hides SubmissionQueue and CompletionQueue behind standard range interface. We put in SubmissionEntry entries and take out CompletionEntry entries.

Use predefined prepXX methods to fill required fields of SubmissionEntry before put or during putWith.

Note

prepXX functions doesn't touch previous entry state, just fills in operation properties. This is because for less error prone interface it is cleared automatically when prepared using putWith. So when using on own SubmissionEntry (outside submission queue), that would be added to the submission queue using put, be sure its cleared if it's reused for multiple operations.

Uring
(local variable) during.Uring io
io
;
const
(local variable) const(int) setupRet
setupRet
=
(local variable) during.Uring io
io
.
int during.setup(ref during.Uring uring, uint entries = 128u, during.io_uring.SetupFlags flags = SetupFlags.NONE) nothrow @nogc @safe

Setup new instance of io_uring into provided Uring structure.

@paramuring Uring structure to be initialized (must not be already initialized)@paramentries Number of entries to initialize uring with@paramflags SetupFlags to use to initialize uring.@returnsOn succes it returns 0, -errno otherwise.
setup
(8);
if (
(local variable) const(int) setupRet
setupRet
< 0)
{
void std.stdio.writefln!(char, const(int))(in char[] fmt, const(int) __param_1) @safe

Equivalent to writef(fmt, args, '\n').

writefln
("SKIP: io_uring_setup failed (errno %d) — io_uring unavailable on this host", -
(local variable) const(int) setupRet
setupRet
);
return 0; } // Two distinct futex words. Both armed with val=0, matching their current // value, so the wait genuinely parks (a mismatch would return -EAGAIN). uint[2]
(local variable) uint[2] words
words
= [0, 0];
// The vector of futexes to wait on. FUTEX2_SIZE_U32 selects a 32-bit futex // (the only size most arches support); FUTEX2_PRIVATE puts it in the // process-private hash so a legacy FUTEX_WAKE_PRIVATE on the same address // can match it.
(struct) during.io_uring.futex_waitv

Single entry passed to IORING_OP_FUTEX_WAITV. Mirrors struct futex_waitv`` from <linux/futex.h>.

Note

Available from Linux 6.7

futex_waitv
[2]
(local variable) during.io_uring.futex_waitv[2] vec
vec
;
foreach (
(local variable) int i
i
; 0 .. 2)
{
(local variable) during.io_uring.futex_waitv[2] vec
vec
[
(local variable) int i
i
].
(field) ulong during.io_uring.futex_waitv.val

expected value of the futex

val
= 0; // expected value — must equal *uaddr for the wait to arm
(local variable) during.io_uring.futex_waitv[2] vec
vec
[
(local variable) int i
i
].
(field) ulong during.io_uring.futex_waitv.uaddr

pointer to the futex word

uaddr
= cast(ulong) &
(local variable) uint[2] words
words
[
(local variable) int i
i
];
(local variable) during.io_uring.futex_waitv[2] vec
vec
[
(local variable) int i
i
].
(field) uint during.io_uring.futex_waitv.flags

FUTEX2_SIZE_* (+ FUTEX2_PRIVATE / FUTEX2_NUMA)

flags
=
(constant) int during.io_uring.FUTEX2_SIZE_U32 = 2

32-bit futex (the only size supported on most arches today)

FUTEX2_SIZE_U32
|
(constant) int during.io_uring.FUTEX2_PRIVATE = 128

process-private (skips NUMA hash lookup)

FUTEX2_PRIVATE
;
} // Spin up the helper that will wake the *second* word. FUTEX_WAITV completes // when ANY entry is woken, so waking words[1] must complete the whole wait.
(struct) io_uring_futex_waitv.WakeCtx
WakeCtx
(local variable) io_uring_futex_waitv.WakeCtx ctx
ctx
;
(local variable) io_uring_futex_waitv.WakeCtx ctx
ctx
.
(field) uint* io_uring_futex_waitv.WakeCtx.word
word
= &
(local variable) uint[2] words
words
[1];
(alias) core.sys.posix.sys.types.pthread_t = ulong
pthread_t
(local variable) ulong tid
tid
;
const
(local variable) const(int) pr
pr
=
int core.sys.posix.pthread.pthread_create(ulong*, scope const(core.sys.posix.sys.types.pthread_attr_t*), extern (C) void* function(void*), void*) nothrow @nogc
pthread_create
(&
(local variable) ulong tid
tid
, null, &
void* io_uring_futex_waitv.wakeWorker(void* arg) nothrow @nogc @system
wakeWorker
, &
(local variable) io_uring_futex_waitv.WakeCtx ctx
ctx
);
if (
(local variable) const(int) pr
pr
!= 0)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("pthread_create failed: errno %d",
(local variable) const(int) pr
pr
);
return 1; } // Submit the vectored wait, linked to a TIMEOUT so the ring is never blocked // indefinitely: if the wake never lands, the TIMEOUT fires and unblocks // `wait`, and the wait CQE comes back -ECANCELED. IO_LINK means the timeout // is the deadline for the preceding waitv. enum ulong
(constant) ulong io_uring_futex_waitv.main.WAITV_DATA = 61566LU
WAITV_DATA
= 0xF07E;
enum ulong
(constant) ulong io_uring_futex_waitv.main.TIMEOUT_DATA = 29152LU
TIMEOUT_DATA
= 0x71_E0;
(local variable) during.Uring io
io
.putWith!(
(ref SubmissionEntry e, futex_waitv* v) { e.prepFutexWaitv(v, 2, 0); // wait on both entries; wake on any e.user_data = WAITV_DATA; e.flags |= SubmissionEntryFlags.IO_LINK; })(&
during.Uring during.Uring.putWith!(function (ref during.io_uring.SubmissionEntry e, during.io_uring.futex_waitv* v) nothrow @nogc @safe { prepFutexWaitv(e, cast(const(during.io_uring.futex_waitv)*)v, 2u, 0u); e.user_data = 61566LU; cast(int)e.flags |= 4; } , during.io_uring.futex_waitv*)(during.io_uring.futex_waitv* __param_0) nothrow @nogc return ref @safe

Adds new entry to the SubmissionQueue.

Note that this just adds entry to the queue and doesn't advance the tail marker kernel sees. For that finishSq() is needed to be called next.

Also note that to actually enter new entries to kernel, it's needed to call submit().

@paramFN Function to fill next entry in queue by ref (should be faster). It is expected to be in a form of void function(ARGS)(ref SubmissionEntry, auto ref ARGS). Note that in this case queue entry is cleaned first before function is called.@paramentry Custom built SubmissionEntry to be posted as is. Note that in this case it is copied whole over one in the SubmissionQueue.@paramargs Optional arguments passed to the function@returnsreference to Uring structure so it's possible to chain multiple commands.
vec
[0]);
// 1.5s ceiling — comfortably above the helper's first 5ms attempt, well // under the 2s budget. KernelTimespec is {tv_sec, tv_nsec}.
(struct) during.io_uring.KernelTimespec

Time specification as defined in kernel headers (used by TIMEOUT operations)

KernelTimespec
(local variable) during.io_uring.KernelTimespec ts
ts
= { tv_sec: 1, tv_nsec: 500_000_000 };
(local variable) during.Uring io
io
.putWith!(
(ref SubmissionEntry e, KernelTimespec* t) { e.prepTimeout(*t, 0, TimeoutFlags.REL); e.user_data = TIMEOUT_DATA; })(&
during.Uring during.Uring.putWith!(function (ref during.io_uring.SubmissionEntry e, during.io_uring.KernelTimespec* t) nothrow @nogc @safe { prepTimeout(e, *t, 0LU, TimeoutFlags.REL); e.user_data = 29152LU; } , during.io_uring.KernelTimespec*)(during.io_uring.KernelTimespec* __param_0) nothrow @nogc return ref @safe

Adds new entry to the SubmissionQueue.

Note that this just adds entry to the queue and doesn't advance the tail marker kernel sees. For that finishSq() is needed to be called next.

Also note that to actually enter new entries to kernel, it's needed to call submit().

@paramFN Function to fill next entry in queue by ref (should be faster). It is expected to be in a form of void function(ARGS)(ref SubmissionEntry, auto ref ARGS). Note that in this case queue entry is cleaned first before function is called.@paramentry Custom built SubmissionEntry to be posted as is. Note that in this case it is copied whole over one in the SubmissionQueue.@paramargs Optional arguments passed to the function@returnsreference to Uring structure so it's possible to chain multiple commands.
ts
);
const
(local variable) const(int) submitted
submitted
=
(local variable) during.Uring io
io
.
int during.Uring.submit(uint want) nothrow @nogc @safe

Submits qued SubmissionEntry to be processed by kernel.

@paramwant number of CompletionEntries to wait for. If 0, this just submits queued entries and returns. If > 0, it blocks until at least wanted number of entries were completed.@paramsig See io_uring_enter(2) man page@returnsNumber of submitted entries on success, -errno on error
submit
(0);
if (
(local variable) const(int) submitted
submitted
< 0)
{
void core.atomic.atomicStore!(MemoryOrder.rel, int, int)(ref shared(int) val, int newval) pure nothrow @nogc @trusted

Writes 'newval' into 'val'. The memory barrier specified by 'ms' is applied to the operation, which is fully sequenced by default. Valid memory orders are MemoryOrder.raw, MemoryOrder.rel, and MemoryOrder.seq.

@paramval The target variable.@paramnewval The value to store.
atomicStore
!(
(enum) core.atomic.MemoryOrder

Specifies the memory ordering semantics of an atomic operation.

@see
MemoryOrder
.
(enum value) core.atomic.MemoryOrder.rel = 3

Sink-load + sink-store barrier. Corresponds to LLVM AtomicOrdering.Release and C++11/C11 memory_order_release.

rel
)(
(local variable) io_uring_futex_waitv.WakeCtx ctx
ctx
.
(field) shared(int) io_uring_futex_waitv.WakeCtx.stop
stop
, 1);
int core.sys.posix.pthread.pthread_join(ulong, void**) nothrow @nogc
pthread_join
(
(local variable) ulong tid
tid
, null);
stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("submit failed: errno %d", -
(local variable) const(int) submitted
submitted
);
return 1; } // Drain both completions (the waitv and the linked timeout). We need at most // two CQEs; capture the waitv result by user_data. int
(local variable) int waitvRes
waitvRes
= int.
(constant) int int.min = -2147483648
min
;
bool
(local variable) bool sawWaitv
sawWaitv
= false;
foreach (
(local variable) int _
_
; 0 .. 2)
{
(local variable) during.Uring io
io
.
int during.Uring.wait(uint want = 1u) nothrow @nogc

Simmilar to submit but with this method we just wait for required number of CompletionEntries.

@returns0 on success, -errno on error
wait
(1);
const
(local variable) const(during.io_uring.CompletionEntry) cqe
cqe
=
(local variable) during.Uring io
io
.
during.io_uring.CompletionEntry during.Uring.front() pure nothrow @nogc return ref @safe

Get first CompletionEntry from cq ring

front
;
if (
(local variable) const(during.io_uring.CompletionEntry) cqe
cqe
.
(field) ulong during.io_uring.CompletionEntry.user_data

sqe->data submission passed back

user_data
==
(constant) ulong io_uring_futex_waitv.main.WAITV_DATA = 61566LU
WAITV_DATA
)
{
(local variable) int waitvRes
waitvRes
=
(local variable) const(during.io_uring.CompletionEntry) cqe
cqe
.
(field) int during.io_uring.CompletionEntry.res

result code for this event

res
;
(local variable) bool sawWaitv
sawWaitv
= true;
}
(local variable) during.Uring io
io
.
void during.Uring.popFront() pure nothrow @nogc @safe

Move to next CompletionEntry

popFront
();
if (
(local variable) bool sawWaitv
sawWaitv
) break; // got what we came for; the timeout CQE (if any) can drain on exit
} // Tell the helper to stop and join it before inspecting results.
void core.atomic.atomicStore!(MemoryOrder.rel, int, int)(ref shared(int) val, int newval) pure nothrow @nogc @trusted

Writes 'newval' into 'val'. The memory barrier specified by 'ms' is applied to the operation, which is fully sequenced by default. Valid memory orders are MemoryOrder.raw, MemoryOrder.rel, and MemoryOrder.seq.

@paramval The target variable.@paramnewval The value to store.
atomicStore
!(
(enum) core.atomic.MemoryOrder

Specifies the memory ordering semantics of an atomic operation.

@see
MemoryOrder
.
(enum value) core.atomic.MemoryOrder.rel = 3

Sink-load + sink-store barrier. Corresponds to LLVM AtomicOrdering.Release and C++11/C11 memory_order_release.

rel
)(
(local variable) io_uring_futex_waitv.WakeCtx ctx
ctx
.
(field) shared(int) io_uring_futex_waitv.WakeCtx.stop
stop
, 1);
int core.sys.posix.pthread.pthread_join(ulong, void**) nothrow @nogc
pthread_join
(
(local variable) ulong tid
tid
, null);
if (!
(local variable) bool sawWaitv
sawWaitv
)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("no FUTEX_WAITV completion observed");
return 1; } // -EINVAL / -EOPNOTSUPP / -ENOSYS => the op isn't supported on this kernel. if (
(local variable) int waitvRes
waitvRes
== -
(constant) int core.stdc.errno.EINVAL = 22
EINVAL
||
(local variable) int waitvRes
waitvRes
== -
(constant) int core.stdc.errno.EOPNOTSUPP = 95
EOPNOTSUPP
||
(local variable) int waitvRes
waitvRes
== -
(constant) int core.stdc.errno.ENOSYS = 38
ENOSYS
)
{
void std.stdio.writefln!(char, int)(in char[] fmt, int __param_1) @safe

Equivalent to writef(fmt, args, '\n').

writefln
("SKIP: IORING_OP_FUTEX_WAITV unsupported on this kernel (res=%d)",
(local variable) int waitvRes
waitvRes
);
return 0; } // A successful wake reports res >= 0 (the kernel returns the index of the // woken futex). A negative value here means the wait was cancelled by the // timeout (the helper never managed to wake it) or another genuine failure. if (
(local variable) int waitvRes
waitvRes
< 0)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("FUTEX_WAITV was not woken: res=%d (errno %d), helper made %d attempts",
(local variable) int waitvRes
waitvRes
, -
(local variable) int waitvRes
waitvRes
,
(local variable) io_uring_futex_waitv.WakeCtx ctx
ctx
.
(field) int io_uring_futex_waitv.WakeCtx.attempts
attempts
);
return 1; }
void std.stdio.writefln!(char, int, int)(in char[] fmt, int __param_1, int __param_2) @safe

Equivalent to writef(fmt, args, '\n').

writefln
("ok: FUTEX_WAITV woken (res=%d, woken futex index) after %d helper wake attempt(s)",
(local variable) int waitvRes
waitvRes
,
(local variable) io_uring_futex_waitv.WakeCtx ctx
ctx
.
(field) int io_uring_futex_waitv.WakeCtx.attempts
attempts
);
return 0; }