#!/usr/bin/env dub
/+ dub.sdl:
name "io_uring_read_multishot"
dependency "during" version="~>0.5.0"
platforms "linux"
targetPath "build"
+/
/**
* `io_uring` — multishot read into ring-provided buffers
* (`IORING_OP_READ_MULTISHOT`, Linux 6.7).
*
* A normal `READ` SQE produces one read and one CQE. `READ_MULTISHOT` stays
* armed against a *pollable* fd (a pipe here): each time data arrives the kernel
* pops a buffer from a registered buffer group, reads one chunk into it, and
* posts a CQE — *without* re-submitting an SQE. While the request remains armed
* each CQE carries `CQEFlags.MORE` (more completions to come) and
* `CQEFlags.BUFFER` (a kernel-chosen buffer id is packed into the upper 16 bits
* of `cqe.flags`). The request terminates — final CQE without `MORE` — on EOF,
* on error, or when the buffer group runs dry (`-ENOBUFS`).
*
* This program:
* 1. registers a buffer ring (`registerBufRing`, the 5.19 fast path) for group
* `BGID` and publishes a handful of equal-sized buffers,
* 2. arms a single `READ_MULTISHOT` on the read end of a pipe with
* buffer-select (`prepReadMultishot` sets `IOSQE_BUFFER_SELECT` + `buf_group`),
* 3. writes two separate chunks into the write end,
* 4. waits for the two resulting CQEs and asserts each one set `MORE|BUFFER`
* and landed its chunk in exactly the buffer id the kernel reported.
*
* The "one SQE, many CQEs" shape is the whole point: the cost of arming a read
* is paid once, and steady-state reads avoid the submit half of the syscall.
*
* 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 read-multishot.d`
*
* Portability: prints `SKIP:` and exits 0 when io_uring is unavailable, when the
* op is unknown to `probe()`, or when an op/register call reports
* -EINVAL/-EOPNOTSUPP/-ENOSYS (kernel predates 6.7). Exits nonzero only on a
* genuinely unexpected syscall failure.
*/
module (module) io_uring_read_multishotio_uring — multishot read into ring-provided buffers
(IORING_OP_READ_MULTISHOT, Linux 6.7).
A normal READ SQE produces one read and one CQE. READ_MULTISHOT stays
armed against a pollable fd (a pipe here): each time data arrives the kernel
pops a buffer from a registered buffer group, reads one chunk into it, and
posts a CQE — without re-submitting an SQE. While the request remains armed
each CQE carries CQEFlags.MORE (more completions to come) and
CQEFlags.BUFFER (a kernel-chosen buffer id is packed into the upper 16 bits
of cqe.flags). The request terminates — final CQE without MORE — on EOF,
on error, or when the buffer group runs dry (-ENOBUFS).
This program:
registers a buffer ring (registerBufRing, the 5.19 fast path) for group
BGID and publishes a handful of equal-sized buffers,
arms a single READ_MULTISHOT on the read end of a pipe with
buffer-select (prepReadMultishot sets IOSQE_BUFFER_SELECT + buf_group),
writes two separate chunks into the write end,
waits for the two resulting CQEs and asserts each one set MORE|BUFFER
and landed its chunk in exactly the buffer id the kernel reported.
The "one SQE, many CQEs" shape is the whole point: the cost of arming a read
is paid once, and steady-state reads avoid the submit half of the syscall.
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 read-multishot.d
Portability
prints SKIP: and exits 0 when io_uring is unavailable, when the
op is unknown to probe(), or when an op/register call reports
-EINVAL/-EOPNOTSUPP/-ENOSYS (kernel predates 6.7). Exits nonzero only on a
genuinely unexpected syscall failure.
io_uring_read_multishot;
import (module) duringSimple idiomatic dlang wrapper around linux io_uring
(see: https://kernel.dk/io_uring.pdf) asynchronous API.
during;
import (package) corecore.(package) core.stdcstdc.(module) core.stdc.errnoD header file for C99.
pubs.opengroup.org/onlinepubs/009695399/basedefs/errno.h.html, errno.h
Source
core/stdc/errno.d
errno : (alias constant) io_uring_read_multishot.EINVAL = int core.stdc.errno.EINVAL = 22EINVAL, (alias constant) io_uring_read_multishot.ENOSYS = int core.stdc.errno.ENOSYS = 38ENOSYS, (alias constant) io_uring_read_multishot.EOPNOTSUPP = int core.stdc.errno.EOPNOTSUPP = 95EOPNOTSUPP;
import (package) corecore.(package) core.stdcstdc.(module) core.stdc.stdlibD header file for C99.
pubs.opengroup.org/onlinepubs/009695399/basedefs/stdlib.h.html, stdlib.h
Source
core/stdc/stdlib.d
stdlib : (alias) io_uring_read_multishot.free = void core.stdc.stdlib.free(void* ptr) nothrow @nogcfree;
import (package) corecore.(package) core.syssys.(package) core.sys.posixposix.(module) core.sys.posix.stdlibD header file for POSIX.
stdlib : (alias) io_uring_read_multishot.posix_memalign = int core.sys.posix.stdlib.posix_memalign(scope void**, ulong, ulong) pure nothrow @nogcposix_memalign;
import (package) corecore.(package) core.syssys.(package) core.sys.posixposix.(module) core.sys.posix.unistdD header file for POSIX.
unistd : (alias) io_uring_read_multishot.close = int core.sys.posix.unistd.close(int) nothrow @nogc @trustedclose, (alias) io_uring_read_multishot.pipe = int core.sys.posix.unistd.pipe(ref int[2]) nothrow @nogc @trustedpipe, (alias) io_uring_read_multishot.write = long core.sys.posix.unistd.write(int, scope const(void*), ulong) nothrow @nogcwrite;
import (package) stdstd.(module) std.stdioCategory 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:
The lowest layer is the operating system layer. The two main schemes are Windows and Posix.
C's stdio.h which unifies the two operating system schemes.
std.stdio, this module, unifies the various stdio.h implementations into
a high level package for D programs.
Source
std/stdio.d
stdio : stderr, (alias template) io_uring_read_multishot.writefln = std.stdio.writefln(alias fmt, A...)(A args) if (isSomeString!(typeof(fmt)))Equivalent to writef(fmt, args, '\n').
writefln;
// Buffer-ring geometry. RING_ENTRIES must be a power of two — the kernel masks
// the producer tail with `ring_entries - 1`.
enum ushort (constant) ushort io_uring_read_multishot.BGID = cast(ushort)7uBGID = 7;
enum uint (constant) uint io_uring_read_multishot.RING_ENTRIES = 8uRING_ENTRIES = 8;
enum uint (constant) uint io_uring_read_multishot.BUF_SIZE = 64uBUF_SIZE = 64;
enum uint (constant) uint io_uring_read_multishot.CHUNKS = 2uCHUNKS = 2; // number of separate writes -> number of multishot CQEs
int int D main()main()
{
(struct) during.UringMain 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 ioio;
const (local variable) const(int) setupRetsetupRet = (local variable) during.Uring ioio.int during.setup(ref during.Uring uring, uint entries = 128u, during.io_uring.SetupFlags flags = SetupFlags.NONE) nothrow @nogc @safeSetup new instance of io_uring into provided Uring structure.
setup(8);
if ((local variable) const(int) setupRetsetupRet < 0)
{
void std.stdio.writefln!(char, const(int))(in char[] fmt, const(int) __param_1) @safeEquivalent to writef(fmt, args, '\n').
writefln("SKIP: io_uring_setup failed (errno %d) — io_uring unavailable on this host", -(local variable) const(int) setupRetsetupRet);
return 0;
}
// Fast unsupported-feature gate: ask the kernel whether it knows the op at
// all. An old kernel (or one without the probe op) trips the SKIP path here
// before we touch any buffers.
auto (local variable) during.Probe probeprobe = (local variable) during.Uring ioio.during.Probe during.Uring.probe() nothrow @nogc @safeProbes supported operations
probe();
if (!cast(bool) (local variable) during.Probe probeprobe || !(local variable) during.Probe probeprobe.bool during.Probe.isSupported(during.io_uring.Operation op) const pure nothrow @nogc @safeIs operation supported?
isSupported((enum) during.io_uring.OperationDescribes the operation to be performed
Operation.(enum value) during.io_uring.Operation.READ_MULTISHOT = cast(ubyte)49uIORING_OP_READ_MULTISHOT - multishot read into a buffer group
READ_MULTISHOT))
{
void std.stdio.writefln!char(in char[] fmt) @safeEquivalent to writef(fmt, args, '\n').
writefln("SKIP: IORING_OP_READ_MULTISHOT not advertised by probe — needs Linux 6.7+");
return 0;
}
// A pipe gives us a *pollable* fd: multishot read re-arms on each readiness
// edge. We read the read end via io_uring and feed it with ordinary write(2)
// on the write end. Loopback-only, no fds beyond this process.
int[2] (local variable) int[2] pp;
if (int core.sys.posix.unistd.pipe(ref int[2]) nothrow @nogc @trustedpipe((local variable) int[2] pp) != 0)
{
stderr.std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @systemwritefln("pipe() failed");
return 1;
}
scope (exit) { int core.sys.posix.unistd.close(int) nothrow @nogc @trustedclose((local variable) int[2] pp[0]); int core.sys.posix.unistd.close(int) nothrow @nogc @trustedclose((local variable) int[2] pp[1]); }
// Allocate the buffer ring page-aligned (IORING_REGISTER_PBUF_RING requires
// page alignment). It is a flat array of RING_ENTRIES `io_uring_buf` slots;
// slot 0's `resv` field doubles as the ring's producer tail.
enum (alias) object.size_t = ulongsize_t (constant) ulong io_uring_read_multishot.main.ringBytes = 128LUringBytes = (struct) during.io_uring.io_uring_bufio_uring_buf.(constant) ulong during.io_uring.io_uring_buf.sizeof = 16LUsizeof * (constant) uint io_uring_read_multishot.RING_ENTRIES = 8uRING_ENTRIES;
void* (local variable) void* ringPtrringPtr;
if (int core.sys.posix.stdlib.posix_memalign(scope void**, ulong, ulong) pure nothrow @nogcposix_memalign(&(local variable) void* ringPtrringPtr, 4096, (constant) ulong io_uring_read_multishot.main.ringBytes = 128LUringBytes) != 0 || (local variable) void* ringPtrringPtr is null)
{
stderr.std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @systemwritefln("posix_memalign failed");
return 1;
}
scope (exit) void core.stdc.stdlib.free(void* ptr) nothrow @nogcfree((local variable) void* ringPtrringPtr);
auto (local variable) during.io_uring.io_uring_buf* ringring = cast((struct) during.io_uring.io_uring_bufio_uring_buf*) (local variable) void* ringPtrringPtr;
(local variable) during.io_uring.io_uring_buf* ringring[0 .. (constant) uint io_uring_read_multishot.RING_ENTRIES = 8uRING_ENTRIES] = (struct) during.io_uring.io_uring_bufio_uring_buf.(constant) during.io_uring.io_uring_buf during.io_uring.io_uring_buf.init = io_uring_buf(0LU, 0u, cast(ushort)0u, cast(ushort)0u)init; // zero whole ring (incl. tail=0)
// Backing storage for the buffers (separate from the 16-byte ring slots).
auto (local variable) ubyte[] storestore = new ubyte[(constant) uint io_uring_read_multishot.BUF_SIZE = 64uBUF_SIZE * (constant) uint io_uring_read_multishot.RING_ENTRIES = 8uRING_ENTRIES];
// Register the buffer ring for group BGID. A pre-6.7 kernel that nonetheless
// lacks buffer rings would fail here; treat the unsupported errnos as SKIP.
(struct) during.io_uring.io_uring_buf_regio_uring_buf_reg (local variable) during.io_uring.io_uring_buf_reg regreg;
(local variable) during.io_uring.io_uring_buf_reg regreg.(field) ulong during.io_uring.io_uring_buf_reg.ring_addrring_addr = cast(ulong) (local variable) void* ringPtrringPtr;
(local variable) during.io_uring.io_uring_buf_reg regreg.(field) uint during.io_uring.io_uring_buf_reg.ring_entriesring_entries = (constant) uint io_uring_read_multishot.RING_ENTRIES = 8uRING_ENTRIES;
(local variable) during.io_uring.io_uring_buf_reg regreg.(field) ushort during.io_uring.io_uring_buf_reg.bgidbgid = (constant) ushort io_uring_read_multishot.BGID = cast(ushort)7uBGID;
const (local variable) const(int) regRetregRet = (local variable) during.Uring ioio.int during.Uring.registerBufRing(ref scope during.io_uring.io_uring_buf_reg reg, uint flags = 0u) nothrow @nogc @trustedRegisters a kernel-side provided-buffer ring (IORING_REGISTER_PBUF_RING). reg must be
filled with the buffer ring address, entry count and group id; flags is OR'd into
reg`.`flags (e.g. IOU_PBUF_RING_INC). Mirrors liburing's io_uring_register_buf_ring.
registerBufRing((local variable) during.io_uring.io_uring_buf_reg regreg);
if ((local variable) const(int) regRetregRet == -(constant) int core.stdc.errno.EINVAL = 22EINVAL || (local variable) const(int) regRetregRet == -(constant) int core.stdc.errno.EOPNOTSUPP = 95EOPNOTSUPP || (local variable) const(int) regRetregRet == -(constant) int core.stdc.errno.ENOSYS = 38ENOSYS)
{
void std.stdio.writefln!(char, const(int))(in char[] fmt, const(int) __param_1) @safeEquivalent to writef(fmt, args, '\n').
writefln("SKIP: IORING_REGISTER_PBUF_RING unsupported (errno %d) — needs Linux 5.19+", -(local variable) const(int) regRetregRet);
return 0;
}
if ((local variable) const(int) regRetregRet < 0)
{
stderr.std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @systemwritefln("registerBufRing failed: errno %d", -(local variable) const(int) regRetregRet);
return 1;
}
scope (exit) (local variable) during.Uring ioio.int during.Uring.unregisterBufRing(int bgid) nothrow @nogc @trustedUnregisters the provided-buffer ring with group id bgid.
Mirrors liburing's io_uring_unregister_buf_ring.
unregisterBufRing((constant) ushort io_uring_read_multishot.BGID = cast(ushort)7uBGID);
// Publish all RING_ENTRIES buffers: each slot points at its BUF_SIZE chunk of
// `store` and carries a distinct buffer id (`bid == index`, so we can recover
// which chunk the kernel filled).
enum uint (constant) uint io_uring_read_multishot.main.mask = 7umask = (constant) uint io_uring_read_multishot.RING_ENTRIES = 8uRING_ENTRIES - 1;
foreach (ushort (local variable) ushort ii; 0 .. cast(ushort) (constant) uint io_uring_read_multishot.RING_ENTRIES = 8uRING_ENTRIES)
{
auto (local variable) during.io_uring.io_uring_buf* slotslot = &(local variable) during.io_uring.io_uring_buf* ringring[(local variable) ushort ii & (constant) uint io_uring_read_multishot.main.mask = 7umask];
(local variable) during.io_uring.io_uring_buf* slotslot.(field) ulong during.io_uring.io_uring_buf.addraddr = cast(ulong) &(local variable) ubyte[] storestore[(local variable) ushort ii * (constant) uint io_uring_read_multishot.BUF_SIZE = 64uBUF_SIZE];
(local variable) during.io_uring.io_uring_buf* slotslot.(field) uint during.io_uring.io_uring_buf.lenlen = (constant) uint io_uring_read_multishot.BUF_SIZE = 64uBUF_SIZE;
(local variable) during.io_uring.io_uring_buf* slotslot.(field) ushort during.io_uring.io_uring_buf.bidbid = (local variable) ushort ii;
}
// Advance the producer tail by the count of published buffers (tail lives in
// slot 0, overlaying io_uring_buf.resv).
(local variable) during.io_uring.io_uring_buf* ringring[0].(field) ushort during.io_uring.io_uring_buf.resvresv = cast(ushort) (constant) uint io_uring_read_multishot.RING_ENTRIES = 8uRING_ENTRIES;
// Arm one multishot READ on the pipe read end, selecting from group BGID.
// `prepReadMultishot` sets IOSQE_BUFFER_SELECT + buf_group for us. After this
// single submit the kernel re-arms itself across completions.
// The kernel's READ_MULTISHOT prep rejects a non-zero `len` (sqe->len must be
// 0) and treats a `-1` offset as "use the file position" — the right choice
// for a stream like a pipe. The per-read size is whatever buffer the group
// hands out (BUF_SIZE here), not an SQE field.
enum ulong (constant) ulong io_uring_read_multishot.main.cookie = 1856844631LUcookie = 0x6EAD_3357;
(local variable) during.Uring ioio.putWith!((ref SubmissionEntry e, int readFd) {
e.prepReadMultishot(readFd, 0, -1, BGID);
e.user_data = cookie;
})(during.Uring during.Uring.putWith!(function (ref during.io_uring.SubmissionEntry e, int readFd) nothrow @nogc @safe
{
prepReadMultishot(e, readFd, 0u, -1L, cast(ushort)7u);
e.user_data = 1856844631LU;
}
, int)(ref int __param_0) nothrow @nogc return ref @safeAdds 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().
p[0]);
const (local variable) const(int) submittedsubmitted = (local variable) during.Uring ioio.int during.Uring.submit(uint want) nothrow @nogc @safeSubmits qued SubmissionEntry to be processed by kernel.
submit(0); // flush SQ without blocking
if ((local variable) const(int) submittedsubmitted < 0)
{
stderr.std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @systemwritefln("submit failed: errno %d", -(local variable) const(int) submittedsubmitted);
return 1;
}
// Write CHUNKS distinct payloads. Each readiness edge drives one multishot
// read -> one CQE. We write them one at a time and drain the CQE in between
// so the two chunks don't coalesce into a single read.
static immutable (alias) object.string = stringstring[(constant) uint io_uring_read_multishot.CHUNKS = 2uCHUNKS] (immutable global) immutable(string[2]) io_uring_read_multishot.main.payloadspayloads = ["first-chunk", "second-chunk!!"];
foreach ((parameter) ulong idxidx, (parameter) immutable(string) payloadpayload; (immutable global) immutable(string[2]) io_uring_read_multishot.main.payloadspayloads)
{
const (local variable) const(long) wrotewrote = long core.sys.posix.unistd.write(int, scope const(void*), ulong) nothrow @nogcwrite((local variable) int[2] pp[1], &(local variable) immutable(string) payloadpayload[0], (local variable) immutable(string) payloadpayload.(field) ulong immutable(string).lengthlength);
if ((local variable) const(long) wrotewrote != cast((alias) object.ptrdiff_t = longptrdiff_t) (local variable) immutable(string) payloadpayload.(field) ulong immutable(string).lengthlength)
{
stderr.std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @systemwritefln("write to pipe failed: %d", (local variable) const(long) wrotewrote);
return 1;
}
// Exactly one CQE is imminent per write — bounded wait.
(local variable) during.Uring ioio.int during.Uring.wait(uint want = 1u) nothrow @nogcSimmilar to submit but with this method we just wait for required number
of CompletionEntries.
wait(1);
const (local variable) const(during.io_uring.CompletionEntry) cc = (local variable) during.Uring ioio.during.io_uring.CompletionEntry during.Uring.front() pure nothrow @nogc return ref @safeGet first CompletionEntry from cq ring
front;
const (local variable) const(int) resres = (local variable) const(during.io_uring.CompletionEntry) cc.(field) int during.io_uring.CompletionEntry.resresult code for this event
res;
const (local variable) const(during.io_uring.CQEFlags) flagsflags = (local variable) const(during.io_uring.CompletionEntry) cc.(field) during.io_uring.CQEFlags during.io_uring.CompletionEntry.flagsflags;
const (local variable) const(ulong) echoedechoed = (local variable) const(during.io_uring.CompletionEntry) cc.(field) ulong during.io_uring.CompletionEntry.user_datasqe->data submission passed back
user_data;
(local variable) during.Uring ioio.void during.Uring.popFront() pure nothrow @nogc @safeMove to next CompletionEntry
popFront();
if ((local variable) const(ulong) echoedechoed != (constant) ulong io_uring_read_multishot.main.cookie = 1856844631LUcookie)
{
stderr.std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @systemwritefln("user_data mismatch: expected 0x%X, got 0x%X", (constant) ulong io_uring_read_multishot.main.cookie = 1856844631LUcookie, (local variable) const(ulong) echoedechoed);
return 1;
}
// -EINVAL/-EOPNOTSUPP slipping past the probe means the op truly is not
// wired up on this kernel: SKIP rather than fail.
if ((local variable) const(int) resres == -(constant) int core.stdc.errno.EINVAL = 22EINVAL || (local variable) const(int) resres == -(constant) int core.stdc.errno.EOPNOTSUPP = 95EOPNOTSUPP || (local variable) const(int) resres == -(constant) int core.stdc.errno.ENOSYS = 38ENOSYS)
{
void std.stdio.writefln!(char, const(int))(in char[] fmt, const(int) __param_1) @safeEquivalent to writef(fmt, args, '\n').
writefln("SKIP: READ_MULTISHOT rejected at runtime (errno %d) — needs Linux 6.7+", -(local variable) const(int) resres);
return 0;
}
// -ENOBUFS would mean our buffer group ran dry — a bug in our bookkeeping
// here (we published far more buffers than chunks), so it's a hard error.
if ((local variable) const(int) resres < 0)
{
stderr.std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @systemwritefln("READ_MULTISHOT chunk %d failed: errno %d", (local variable) ulong idxidx, -(local variable) const(int) resres);
return 1;
}
if ((local variable) const(int) resres != cast(int) (local variable) immutable(string) payloadpayload.(field) ulong immutable(string).lengthlength)
{
stderr.std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @systemwritefln("chunk %d byte count: expected %d, got %d", (local variable) ulong idxidx, (local variable) immutable(string) payloadpayload.(field) ulong immutable(string).lengthlength, (local variable) const(int) resres);
return 1;
}
// Multishot contract: each in-flight CQE must carry BUFFER (a buffer was
// selected) and MORE (the request stays armed for the next chunk).
if (!((local variable) const(during.io_uring.CQEFlags) flagsflags & (enum) during.io_uring.CQEFlagsFlags used with CompletionEntry
CQEFlags.(enum value) during.io_uring.CQEFlags.BUFFER = 1uIORING_CQE_F_BUFFER (from Linux 5.7)
If set, the upper 16 bits are the buffer ID
BUFFER))
{
stderr.std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @systemwritefln("chunk %d: missing IORING_CQE_F_BUFFER (flags=0x%X)", (local variable) ulong idxidx, cast(uint) (local variable) const(during.io_uring.CQEFlags) flagsflags);
return 1;
}
if (!((local variable) const(during.io_uring.CQEFlags) flagsflags & (enum) during.io_uring.CQEFlagsFlags used with CompletionEntry
CQEFlags.(enum value) during.io_uring.CQEFlags.MORE = 2uIORING_CQE_F_MORE (from Linux 5.13)
If set, parent SQE will generate more CQE entries
MORE))
{
stderr.std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @systemwritefln("chunk %d: multishot disarmed early, missing IORING_CQE_F_MORE (flags=0x%X)",
(local variable) ulong idxidx, cast(uint) (local variable) const(during.io_uring.CQEFlags) flagsflags);
return 1;
}
// Recover the kernel-chosen buffer id from the upper 16 bits and confirm
// the bytes landed in that exact buffer.
const (local variable) const(ushort) bidbid = cast(ushort)(cast(uint) (local variable) const(during.io_uring.CQEFlags) flagsflags >> (enum value) during.io_uring.CQE_BUFFER_SHIFT = 16Note
available from Linux 5.7
CQE_BUFFER_SHIFT);
if ((local variable) const(ushort) bidbid >= (constant) uint io_uring_read_multishot.RING_ENTRIES = 8uRING_ENTRIES)
{
stderr.std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @systemwritefln("chunk %d: out-of-range buffer id %d", (local variable) ulong idxidx, (local variable) const(ushort) bidbid);
return 1;
}
auto (local variable) ubyte[] gotgot = (local variable) ubyte[] storestore[(local variable) const(ushort) bidbid * (constant) uint io_uring_read_multishot.BUF_SIZE = 64uBUF_SIZE .. (local variable) const(ushort) bidbid * (constant) uint io_uring_read_multishot.BUF_SIZE = 64uBUF_SIZE + (local variable) const(int) resres];
if (cast(const(char)[]) (local variable) ubyte[] gotgot != (local variable) immutable(string) payloadpayload)
{
stderr.std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @systemwritefln("chunk %d: payload mismatch in buffer %d", (local variable) ulong idxidx, (local variable) const(ushort) bidbid);
return 1;
}
void std.stdio.writefln!(char, ulong, const(ushort), const(int), const(char)[])(in char[] fmt, ulong __param_1, const(ushort) __param_2, const(int) __param_3, const(char)[] __param_4) @safeEquivalent to writef(fmt, args, '\n').
writefln("ok: multishot chunk %d -> buffer id %d (%d bytes, MORE|BUFFER set): \"%s\"",
(local variable) ulong idxidx, (local variable) const(ushort) bidbid, (local variable) const(int) resres, cast(const(char)[]) (local variable) ubyte[] gotgot);
}
void std.stdio.writefln!(char, uint, uint)(in char[] fmt, uint __param_1, uint __param_2) @safeEquivalent to writef(fmt, args, '\n').
writefln("ok: one READ_MULTISHOT SQE produced %d completions across %d buffers",
(constant) uint io_uring_read_multishot.CHUNKS = 2uCHUNKS, (constant) uint io_uring_read_multishot.RING_ENTRIES = 8uRING_ENTRIES);
return 0;
}