multishot-recv.dhover×231all
#!/usr/bin/env dub
/+ dub.sdl:
    name "io_uring_multishot_recv"
    dependency "during" version="~>0.5.0"
    platforms "linux"
    targetPath "build"
+/
/**
 * `io_uring` — multishot RECV into a buffer ring (`IORING_RECV_MULTISHOT`, Linux 6.0).
 *
 * A plain `RECV` (5.6) consumes one SQE per received segment: you re-arm it for
 * every read. Multishot RECV (6.0) flips that — a *single* armed SQE stays live
 * and posts a fresh CQE for each incoming segment, each one selecting a buffer
 * from a provided-buffer ring. The kernel keeps the operation armed (signalled by
 * `CQEFlags.MORE` on every non-final CQE) so a server can drain a busy socket with
 * one submission instead of N.
 *
 * This program builds directly on the 5.19 buffer-ring example:
 *   1. registers a small buffer ring (`registerBufRing`) for group id `BGID`,
 *   2. publishes several buffers into that ring,
 *   3. arms ONE multishot RECV that selects from the group
 *      (`prepRecvMultishot(fd, gid, len)` sets `IOSQE_BUFFER_SELECT`, `buf_group`,
 *      and `IORING_RECV_MULTISHOT` for us),
 *   4. writes TWO separate messages into the peer end of a socketpair,
 *   5. waits and asserts it gets TWO CQEs from that one SQE, each carrying
 *      `CQEFlags.MORE` (still armed) + `CQEFlags.BUFFER` (a buffer was selected),
 *      landing in two *distinct* ring buffers with the right bytes.
 *
 * The MORE flag is the multishot contract: it stays set while the op is armed and
 * clears on the terminal CQE. The selected buffer id is in the upper 16 bits of
 * `cqe.flags` (`>> CQE_BUFFER_SHIFT`), exactly as for single-shot buffer-select.
 *
 * Companion to the io_uring chronology:
 * see docs/research/async-io/io-uring/timeline.md
 *   § "6.0 — Zero-copy send, single-issuer, sync cancel (October 2022)".
 *
 * Run with: `dub run --single multishot-recv.d`
 *
 * Portability: prints `SKIP:` and exits 0 when io_uring is unavailable or the
 * running kernel predates multishot RECV (the RECV CQE comes back -EINVAL on a
 * pre-6.0 kernel, or registerBufRing -> -EINVAL/-EOPNOTSUPP/-ENOSYS pre-5.19).
 * Exits nonzero only on a genuinely unexpected syscall failure.
 */
module 
(module) io_uring_multishot_recv

io_uring — multishot RECV into a buffer ring (IORING_RECV_MULTISHOT, Linux 6.0).

A plain RECV (5.6) consumes one SQE per received segment: you re-arm it for every read. Multishot RECV (6.0) flips that — a single armed SQE stays live and posts a fresh CQE for each incoming segment, each one selecting a buffer from a provided-buffer ring. The kernel keeps the operation armed (signalled by CQEFlags.MORE on every non-final CQE) so a server can drain a busy socket with one submission instead of N.

This program builds directly on the 5.19 buffer-ring example:

  1. registers a small buffer ring (registerBufRing) for group id BGID,

  2. publishes several buffers into that ring,

  3. arms ONE multishot RECV that selects from the group (prepRecvMultishot(fd, gid, len) sets IOSQE_BUFFER_SELECT, buf_group, and IORING_RECV_MULTISHOT for us),

  4. writes TWO separate messages into the peer end of a socketpair,

  5. waits and asserts it gets TWO CQEs from that one SQE, each carrying CQEFlags.MORE (still armed) + CQEFlags.BUFFER (a buffer was selected), landing in two distinct ring buffers with the right bytes.

The MORE flag is the multishot contract: it stays set while the op is armed and clears on the terminal CQE. The selected buffer id is in the upper 16 bits of cqe.flags (>> CQE_BUFFER_SHIFT), exactly as for single-shot buffer-select.

Companion to the io_uring chronology: see docs/research/async-io/io-uring/timeline.md § "6.0 — Zero-copy send, single-issuer, sync cancel (October 2022)".

Run with: dub run --single multishot-recv.d

Portability

prints SKIP: and exits 0 when io_uring is unavailable or the running kernel predates multishot RECV (the RECV CQE comes back -EINVAL on a pre-6.0 kernel, or registerBufRing -> -EINVAL/-EOPNOTSUPP/-ENOSYS pre-5.19). Exits nonzero only on a genuinely unexpected syscall failure.

io_uring_multishot_recv
;
import
(module) during

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

during
;
import
(package) core
core
.
(package) core.stdc
stdc
.
(module) core.stdc.errno

D header file for C99.

pubs.opengroup.org/onlinepubs/009695399/basedefs/errno.h.html, errno.h

Source

core/stdc/errno.d

@copyrightCopyright Sean Kelly 2005 - 2009.@licenseDistributed under the Boost Software License 1.0. (See accompanying file LICENSE)@authorsSean Kelly, Alex Rønne Petersen@standardsISO/IEC 9899:1999 (E)
errno
:
(alias constant) io_uring_multishot_recv.EINVAL = int core.stdc.errno.EINVAL = 22
EINVAL
,
(alias constant) io_uring_multishot_recv.EOPNOTSUPP = int core.stdc.errno.EOPNOTSUPP = 95
EOPNOTSUPP
,
(alias constant) io_uring_multishot_recv.ENOSYS = int core.stdc.errno.ENOSYS = 38
ENOSYS
;
import
(package) core
core
.
(package) core.stdc
stdc
.
(module) core.stdc.stdlib

D header file for C99.

pubs.opengroup.org/onlinepubs/009695399/basedefs/stdlib.h.html, stdlib.h

Source

core/stdc/stdlib.d

@copyrightCopyright Sean Kelly 2005 - 2014.@licenseDistributed under the Boost Software License 1.0. (See accompanying file LICENSE)@authorsSean Kelly@standardsISO/IEC 9899:1999 (E)
stdlib
:
(alias) io_uring_multishot_recv.free = void core.stdc.stdlib.free(void* ptr) nothrow @nogc
free
;
import
(package) core
core
.
(package) core.sys
sys
.
(package) core.sys.posix
posix
.
(module) core.sys.posix.stdlib

D header file for POSIX.

@copyrightCopyright Sean Kelly 2005 - 2009.@licenseBoost License 1.0.@authorsSean Kelly@standardsThe Open Group Base Specifications Issue 6, IEEE Std 1003.1, 2004 Edition
stdlib
:
(alias) io_uring_multishot_recv.posix_memalign = int core.sys.posix.stdlib.posix_memalign(scope void**, ulong, ulong) pure nothrow @nogc
posix_memalign
;
import
(package) core
core
.
(package) core.sys
sys
.
(package) core.sys.posix
posix
.
(package) core.sys.posix.sys
sys
.
(module) core.sys.posix.sys.socket

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
socket
:
(alias enum value) io_uring_multishot_recv.AF_UNIX = core.sys.posix.sys.socket.AF_UNIX = 1
AF_UNIX
,
(alias enum value) io_uring_multishot_recv.SOCK_STREAM = core.sys.posix.sys.socket.SOCK_STREAM = 1
SOCK_STREAM
,
(alias) io_uring_multishot_recv.socketpair = int core.sys.posix.sys.socket.socketpair(int, int, int, ref int[2]) nothrow @nogc @safe
socketpair
;
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_multishot_recv.close = int core.sys.posix.unistd.close(int) nothrow @nogc @trusted
close
,
(alias) io_uring_multishot_recv.write = long core.sys.posix.unistd.write(int, scope const(void*), ulong) nothrow @nogc
write
;
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
: stderr,
(alias template) io_uring_multishot_recv.writefln = std.stdio.writefln(alias fmt, A...)(A args) if (isSomeString!(typeof(fmt)))

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

writefln
;
// Group id for our buffer ring, and the buffer geometry. RING_ENTRIES must be a // power of two — the kernel masks the tail with `ring_entries - 1`. enum ushort
(constant) ushort io_uring_multishot_recv.BGID = cast(ushort)7u
BGID
= 7;
enum uint
(constant) uint io_uring_multishot_recv.RING_ENTRIES = 8u
RING_ENTRIES
= 8;
enum uint
(constant) uint io_uring_multishot_recv.BUF_SIZE = 64u
BUF_SIZE
= 64;
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; } // A connected pair of local sockets: we recv on sv[0] via io_uring and write // the payloads into sv[1] with ordinary write(2). Loopback-only, no network. int[2]
(local variable) int[2] sv
sv
;
if (
int core.sys.posix.sys.socket.socketpair(int, int, int, ref int[2]) nothrow @nogc @safe
socketpair
(
(enum value) core.sys.posix.sys.socket.AF_UNIX = 1
AF_UNIX
,
(enum value) core.sys.posix.sys.socket.SOCK_STREAM = 1
SOCK_STREAM
, 0,
(local variable) int[2] sv
sv
) != 0)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("socketpair failed");
return 1; } scope (exit) {
int core.sys.posix.unistd.close(int) nothrow @nogc @trusted
close
(
(local variable) int[2] sv
sv
[0]);
int core.sys.posix.unistd.close(int) nothrow @nogc @trusted
close
(
(local variable) int[2] sv
sv
[1]); }
// Allocate the buffer ring page-aligned (the kernel requires page alignment for // IORING_REGISTER_PBUF_RING). 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 = ulong
size_t
(constant) ulong io_uring_multishot_recv.main.ringBytes = 128LU
ringBytes
=
(struct) during.io_uring.io_uring_buf
io_uring_buf
.
(constant) ulong during.io_uring.io_uring_buf.sizeof = 16LU
sizeof
*
(constant) uint io_uring_multishot_recv.RING_ENTRIES = 8u
RING_ENTRIES
;
void*
(local variable) void* ringPtr
ringPtr
;
if (
int core.sys.posix.stdlib.posix_memalign(scope void**, ulong, ulong) pure nothrow @nogc
posix_memalign
(&
(local variable) void* ringPtr
ringPtr
, 4096,
(constant) ulong io_uring_multishot_recv.main.ringBytes = 128LU
ringBytes
) != 0 ||
(local variable) void* ringPtr
ringPtr
is null)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("posix_memalign failed");
return 1; } scope (exit)
void core.stdc.stdlib.free(void* ptr) nothrow @nogc
free
(
(local variable) void* ringPtr
ringPtr
);
auto
(local variable) during.io_uring.io_uring_buf* ring
ring
= cast(
(struct) during.io_uring.io_uring_buf
io_uring_buf
*)
(local variable) void* ringPtr
ringPtr
;
(local variable) during.io_uring.io_uring_buf* ring
ring
[0 ..
(constant) uint io_uring_multishot_recv.RING_ENTRIES = 8u
RING_ENTRIES
] =
(struct) during.io_uring.io_uring_buf
io_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 the whole ring (incl. tail=0)
// Backing storage for the buffers themselves (separate from the ring slots). auto
(local variable) ubyte[] store
store
= new ubyte[
(constant) uint io_uring_multishot_recv.BUF_SIZE = 64u
BUF_SIZE
*
(constant) uint io_uring_multishot_recv.RING_ENTRIES = 8u
RING_ENTRIES
];
// Register the ring with the kernel for group `BGID`.
(struct) during.io_uring.io_uring_buf_reg
io_uring_buf_reg
(local variable) during.io_uring.io_uring_buf_reg reg
reg
;
(local variable) during.io_uring.io_uring_buf_reg reg
reg
.
(field) ulong during.io_uring.io_uring_buf_reg.ring_addr
ring_addr
= cast(ulong)
(local variable) void* ringPtr
ringPtr
;
(local variable) during.io_uring.io_uring_buf_reg reg
reg
.
(field) uint during.io_uring.io_uring_buf_reg.ring_entries
ring_entries
=
(constant) uint io_uring_multishot_recv.RING_ENTRIES = 8u
RING_ENTRIES
;
(local variable) during.io_uring.io_uring_buf_reg reg
reg
.
(field) ushort during.io_uring.io_uring_buf_reg.bgid
bgid
=
(constant) ushort io_uring_multishot_recv.BGID = cast(ushort)7u
BGID
;
const
(local variable) const(int) regRet
regRet
=
(local variable) during.Uring io
io
.
int during.Uring.registerBufRing(ref scope during.io_uring.io_uring_buf_reg reg, uint flags = 0u) nothrow @nogc @trusted

Registers 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.

@returnsOn success, returns 0. On error, -errno.
registerBufRing
(
(local variable) during.io_uring.io_uring_buf_reg reg
reg
);
if (
(local variable) const(int) regRet
regRet
== -
(constant) int core.stdc.errno.EINVAL = 22
EINVAL
||
(local variable) const(int) regRet
regRet
== -
(constant) int core.stdc.errno.EOPNOTSUPP = 95
EOPNOTSUPP
||
(local variable) const(int) regRet
regRet
== -
(constant) int core.stdc.errno.ENOSYS = 38
ENOSYS
)
{
void std.stdio.writefln!(char, const(int))(in char[] fmt, const(int) __param_1) @safe

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

writefln
("SKIP: IORING_REGISTER_PBUF_RING unsupported (errno %d) — needs Linux 5.19+", -
(local variable) const(int) regRet
regRet
);
return 0; } if (
(local variable) const(int) regRet
regRet
< 0)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("registerBufRing failed: errno %d", -
(local variable) const(int) regRet
regRet
);
return 1; } scope (exit)
(local variable) during.Uring io
io
.
int during.Uring.unregisterBufRing(int bgid) nothrow @nogc @trusted

Unregisters the provided-buffer ring with group id bgid. Mirrors liburing's io_uring_unregister_buf_ring.

@returnsOn success, returns 0. On error, -errno.
unregisterBufRing
(
(constant) ushort io_uring_multishot_recv.BGID = cast(ushort)7u
BGID
);
// Publish all RING_ENTRIES buffers into the ring. Each slot points at its // BUF_SIZE chunk of `store` and carries a distinct buffer id (`bid`). The // kernel returns the chosen `bid` in the CQE's upper 16 bits. enum uint
(constant) uint io_uring_multishot_recv.main.mask = 7u
mask
=
(constant) uint io_uring_multishot_recv.RING_ENTRIES = 8u
RING_ENTRIES
- 1;
foreach (ushort
(local variable) ushort i
i
; 0 .. cast(ushort)
(constant) uint io_uring_multishot_recv.RING_ENTRIES = 8u
RING_ENTRIES
)
{ auto
(local variable) during.io_uring.io_uring_buf* slot
slot
= &
(local variable) during.io_uring.io_uring_buf* ring
ring
[
(local variable) ushort i
i
&
(constant) uint io_uring_multishot_recv.main.mask = 7u
mask
];
(local variable) during.io_uring.io_uring_buf* slot
slot
.
(field) ulong during.io_uring.io_uring_buf.addr
addr
= cast(ulong) &
(local variable) ubyte[] store
store
[
(local variable) ushort i
i
*
(constant) uint io_uring_multishot_recv.BUF_SIZE = 64u
BUF_SIZE
];
(local variable) during.io_uring.io_uring_buf* slot
slot
.
(field) uint during.io_uring.io_uring_buf.len
len
=
(constant) uint io_uring_multishot_recv.BUF_SIZE = 64u
BUF_SIZE
;
(local variable) during.io_uring.io_uring_buf* slot
slot
.
(field) ushort during.io_uring.io_uring_buf.bid
bid
=
(local variable) ushort i
i
; // buffer id == index, so we can recover the chunk later
} // Publish: advance the producer tail by the number of buffers we added. The // tail lives in slot 0 (it overlays io_uring_buf.resv there).
(local variable) during.io_uring.io_uring_buf* ring
ring
[0].
(field) ushort during.io_uring.io_uring_buf.resv
resv
= cast(ushort)
(constant) uint io_uring_multishot_recv.RING_ENTRIES = 8u
RING_ENTRIES
;
// Arm ONE multishot RECV that selects buffers from group BGID. The gid overload // sets IOSQE_BUFFER_SELECT + sqe->buf_group, and the multishot wrapper adds // IORING_RECV_MULTISHOT — so this single SQE will keep posting a CQE per segment. enum ulong
(constant) ulong io_uring_multishot_recv.main.recvCookie = 186015461LU
recvCookie
= 0xB16_5EE5;
// Pass `sv[0]` as an explicit arg rather than capturing it: a capturing lambda // would force a GC closure and break `putWith`'s `@nogc`.
(local variable) during.Uring io
io
.putWith!((ref SubmissionEntry e, int recvFd) {
e.prepRecvMultishot(recvFd, BGID, BUF_SIZE); e.user_data = recvCookie; })(
during.Uring during.Uring.putWith!(function (ref during.io_uring.SubmissionEntry e, int recvFd) nothrow @nogc @safe { prepRecvMultishot(e, recvFd, cast(ushort)7u, 64u, MsgFlags.NONE); e.user_data = 186015461LU; } , int)(ref int __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.
sv
[0]);
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); // flush the SQ without blocking on a count
if (
(local variable) const(int) submitted
submitted
< 0)
{ 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; } // Two separate messages to feed the peer end. Each becomes its own readable // segment on sv[0], so the armed multishot RECV should post one CQE per message. immutable ubyte[][2]
(local variable) immutable(ubyte[][2]) payloads
payloads
= [
cast(immutable ubyte[]) "first multishot segment", cast(immutable ubyte[]) "second multishot segment!!", ]; // Drive one segment at a time: write a message, then drain the CQE the armed SQE // produces for it, *before* writing the next. On a stream socket two back-to-back // writes can coalesce into a single readable chunk (and thus a single CQE); // interleaving write-then-drain guarantees two distinct segments — two CQEs from // the one armed SQE, each selecting a fresh ring buffer — deterministically, // without relying on kernel scheduling. We never re-arm: the multishot SQE stays // live across both completions (signalled by CQE_F_MORE). ushort[2]
(local variable) ushort[2] gotBids
gotBids
;
foreach (
(parameter) ulong idx
idx
,
(parameter) immutable(ubyte[]) p
p
;
(local variable) immutable(ubyte[][2]) payloads
payloads
)
{ const
(local variable) const(long) wrote
wrote
=
long core.sys.posix.unistd.write(int, scope const(void*), ulong) nothrow @nogc
write
(
(local variable) int[2] sv
sv
[1], &
(local variable) immutable(ubyte[]) p
p
[0],
(local variable) immutable(ubyte[]) p
p
.
(field) ulong immutable(ubyte[]).length
length
);
if (
(local variable) const(long) wrote
wrote
!= cast(
(alias) object.ptrdiff_t = long
ptrdiff_t
)
(local variable) immutable(ubyte[]) p
p
.
(field) ulong immutable(ubyte[]).length
length
)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("write to peer failed: %d",
(local variable) const(long) wrote
wrote
);
return 1; }
(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); // one CQE is guaranteed imminent (data already written)
const
(local variable) const(during.io_uring.CompletionEntry) c
c
=
(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
;
const
(local variable) const(int) res
res
=
(local variable) const(during.io_uring.CompletionEntry) c
c
.
(field) int during.io_uring.CompletionEntry.res

result code for this event

res
;
const
(local variable) const(during.io_uring.CQEFlags) flags
flags
=
(local variable) const(during.io_uring.CompletionEntry) c
c
.
(field) during.io_uring.CQEFlags during.io_uring.CompletionEntry.flags
flags
;
const
(local variable) const(ulong) echoed
echoed
=
(local variable) const(during.io_uring.CompletionEntry) c
c
.
(field) ulong during.io_uring.CompletionEntry.user_data

sqe->data submission passed back

user_data
;
(local variable) during.Uring io
io
.
void during.Uring.popFront() pure nothrow @nogc @safe

Move to next CompletionEntry

popFront
();
if (
(local variable) const(ulong) echoed
echoed
!=
(constant) ulong io_uring_multishot_recv.main.recvCookie = 186015461LU
recvCookie
)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("user_data mismatch: expected 0x%X, got 0x%X",
(constant) ulong io_uring_multishot_recv.main.recvCookie = 186015461LU
recvCookie
,
(local variable) const(ulong) echoed
echoed
);
return 1; } // A pre-6.0 kernel rejects the multishot bit on the very first CQE. if (
(local variable) const(int) res
res
== -
(constant) int core.stdc.errno.EINVAL = 22
EINVAL
||
(local variable) const(int) res
res
== -
(constant) int core.stdc.errno.EOPNOTSUPP = 95
EOPNOTSUPP
||
(local variable) const(int) res
res
== -
(constant) int core.stdc.errno.ENOSYS = 38
ENOSYS
)
{
void std.stdio.writefln!(char, const(int))(in char[] fmt, const(int) __param_1) @safe

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

writefln
("SKIP: IORING_RECV_MULTISHOT unsupported (errno %d) — needs Linux 6.0+", -
(local variable) const(int) res
res
);
return 0; } if (
(local variable) const(int) res
res
< 0)
{ // -ENOBUFS would mean the ring ran out of published buffers — a real bug // in our bookkeeping, not an unsupported-feature case. stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("multishot RECV CQE #%d failed: errno %d",
(local variable) ulong idx
idx
, -
(local variable) const(int) res
res
);
return 1; } // Multishot contract: each non-terminal CQE carries MORE (still armed). With // two segments and 8 buffers the op cannot exhaust the ring, so both of our // CQEs must be armed. if (!(
(local variable) const(during.io_uring.CQEFlags) flags
flags
&
(enum) during.io_uring.CQEFlags

Flags used with CompletionEntry

CQEFlags
.
(enum value) during.io_uring.CQEFlags.MORE = 2u

IORING_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 @system
writefln
("multishot RECV CQE #%d lost CQE_F_MORE (flags=0x%X) — op disarmed early",
(local variable) ulong idx
idx
, cast(uint)
(local variable) const(during.io_uring.CQEFlags) flags
flags
);
return 1; } // Buffer-select contract: a buffer must have been chosen, with its id in the // upper 16 bits of flags. if (!(
(local variable) const(during.io_uring.CQEFlags) flags
flags
&
(enum) during.io_uring.CQEFlags

Flags used with CompletionEntry

CQEFlags
.
(enum value) during.io_uring.CQEFlags.BUFFER = 1u

IORING_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 @system
writefln
("multishot RECV CQE #%d has no CQE_F_BUFFER (flags=0x%X)",
(local variable) ulong idx
idx
, cast(uint)
(local variable) const(during.io_uring.CQEFlags) flags
flags
);
return 1; } const
(local variable) const(ushort) bid
bid
= cast(ushort)(cast(uint)
(local variable) const(during.io_uring.CQEFlags) flags
flags
>>
(enum value) during.io_uring.CQE_BUFFER_SHIFT = 16

Note

available from Linux 5.7

CQE_BUFFER_SHIFT
);
if (
(local variable) const(ushort) bid
bid
>=
(constant) uint io_uring_multishot_recv.RING_ENTRIES = 8u
RING_ENTRIES
)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("kernel returned out-of-range buffer id %d",
(local variable) const(ushort) bid
bid
);
return 1; } // Confirm the bytes landed in exactly the buffer the kernel selected. auto
(local variable) ubyte[] got
got
=
(local variable) ubyte[] store
store
[
(local variable) const(ushort) bid
bid
*
(constant) uint io_uring_multishot_recv.BUF_SIZE = 64u
BUF_SIZE
..
(local variable) const(ushort) bid
bid
*
(constant) uint io_uring_multishot_recv.BUF_SIZE = 64u
BUF_SIZE
+
(local variable) const(int) res
res
];
if (
(local variable) ubyte[] got
got
!=
(local variable) immutable(ubyte[][2]) payloads
payloads
[
(local variable) ulong idx
idx
])
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("payload #%d mismatch in selected buffer %d",
(local variable) ulong idx
idx
,
(local variable) const(ushort) bid
bid
);
return 1; }
(local variable) ushort[2] gotBids
gotBids
[
(local variable) ulong idx
idx
] =
(local variable) const(ushort) bid
bid
;
} // The whole point of multishot: two segments, two CQEs, from ONE armed SQE — and // each landed in a *distinct* ring buffer (the kernel pops a fresh slot per CQE). if (
(local variable) ushort[2] gotBids
gotBids
[0] ==
(local variable) ushort[2] gotBids
gotBids
[1])
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("expected distinct buffers, both CQEs used buffer id %d",
(local variable) ushort[2] gotBids
gotBids
[0]);
return 1; }
void std.stdio.writefln!(char, ushort, ushort)(in char[] fmt, ushort __param_1, ushort __param_2) @safe

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

writefln
("ok: one armed multishot RECV posted 2 CQEs (MORE+BUFFER) into distinct buffers %d and %d",
(local variable) ushort[2] gotBids
gotBids
[0],
(local variable) ushort[2] gotBids
gotBids
[1]);
return 0; }