cqe-mixed.dhover×92all
#!/usr/bin/env dub
/+ dub.sdl:
    name "io_uring_cqe_mixed"
    dependency "during" version="~>0.5.0"
    platforms "linux"
    targetPath "build"
+/
/**
 * `io_uring` — mixed-size completion queue (`IORING_SETUP_CQE_MIXED`, Linux 6.18).
 *
 * Before 6.18 a ring had to commit, at setup time, to one CQE size for its
 * whole lifetime: either the default 16-byte CQE or — via `IORING_SETUP_CQE32`
 * — a 32-byte CQE for *every* completion. The wide form carries 16 extra bytes
 * of per-completion payload (used by ops like `URING_CMD`), but paying for it
 * ring-wide doubles the CQ memory and the cache footprint of every reaped CQE,
 * even the NOPs and reads that never need the extra room.
 *
 * `IORING_SETUP_CQE_MIXED` removes that all-or-nothing choice: a single ring
 * carries a *mix* of 16- and 32-byte CQEs. Each submission decides its own CQE
 * width, and the kernel tags the wide ones with `IORING_CQE_F_32`
 * (`CQEFlags.F_32`) so the reader can tell them apart while walking the ring.
 *
 * This example sets up a `CQE_MIXED` ring and submits two NOPs:
 *   1. a plain NOP, which completes into a normal 16-byte CQE; and
 *   2. a NOP with `IORING_NOP_CQE32` set in its `nop_flags`, which asks the
 *      kernel for a wide 32-byte CQE — completing with `CQEFlags.F_32` set.
 * Seeing both CQE widths reaped from one ring is the whole point: you pay for
 * the 32 bytes only on the completion that opted in, not on every CQE.
 *
 * Companion to the io_uring chronology:
 * see docs/research/async-io/io-uring/timeline.md
 *     § "6.18 — Mixed-size CQE (≈ November 2025, per tree)".
 *
 * Run with: `dub run --single cqe-mixed.d`
 *
 * Portability: if the running kernel has no `io_uring` at all, or is older than
 * 6.18 and rejects `IORING_SETUP_CQE_MIXED` with `-EINVAL`, the program prints a
 * `SKIP:` line and exits 0 so it stays green in CI regardless of host kernel.
 */
module 
(module) io_uring_cqe_mixed

io_uring — mixed-size completion queue (IORING_SETUP_CQE_MIXED, Linux 6.18).

Before 6.18 a ring had to commit, at setup time, to one CQE size for its whole lifetime: either the default 16-byte CQE or — via IORING_SETUP_CQE32 — a 32-byte CQE for every completion. The wide form carries 16 extra bytes of per-completion payload (used by ops like URING_CMD), but paying for it ring-wide doubles the CQ memory and the cache footprint of every reaped CQE, even the NOPs and reads that never need the extra room.

IORING_SETUP_CQE_MIXED removes that all-or-nothing choice: a single ring carries a mix of 16- and 32-byte CQEs. Each submission decides its own CQE width, and the kernel tags the wide ones with IORING_CQE_F_32 (CQEFlags.F_32) so the reader can tell them apart while walking the ring.

This example sets up a CQE_MIXED ring and submits two NOPs:

  1. a plain NOP, which completes into a normal 16-byte CQE; and

  2. a NOP with IORING_NOP_CQE32 set in its nop_flags, which asks the kernel for a wide 32-byte CQE — completing with CQEFlags.F_32 set.

Seeing both CQE widths reaped from one ring is the whole point: you pay for the 32 bytes only on the completion that opted in, not on every CQE.

Companion to the io_uring chronology: see docs/research/async-io/io-uring/timeline.md § "6.18 — Mixed-size CQE (≈ November 2025, per tree)".

Run with: dub run --single cqe-mixed.d

Portability

if the running kernel has no io_uring at all, or is older than 6.18 and rejects IORING_SETUP_CQE_MIXED with -EINVAL, the program prints a SKIP: line and exits 0 so it stays green in CI regardless of host kernel.

io_uring_cqe_mixed
;
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.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_cqe_mixed.EINVAL = int core.stdc.errno.EINVAL = 22
EINVAL
;
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_cqe_mixed.writefln = std.stdio.writefln(alias fmt, A...)(A args) if (isSomeString!(typeof(fmt)))

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

writefln
, stderr;
// `IORING_OP_NOP` flag — ask the kernel to post a wide 32-byte CQE for this NOP. // Requires the ring to be set up with `CQE32` or `CQE_MIXED`. From Linux 6.18. // (during exposes this as the `IORING_NOP_CQE32` enum; we name it locally for // clarity at the call site.) enum uint
(constant) uint io_uring_cqe_mixed.NOP_WANT_CQE32 = 32u
NOP_WANT_CQE32
=
(constant) uint during.io_uring.IORING_NOP_CQE32 = 32u

IORING_OP_NOP flag — post a 32-byte CQE for this NOP. Requires SetupFlags.CQE32 or CQE_MIXED. From Linux 6.18.

IORING_NOP_CQE32
;
int
int D main()
main
()
{ enum ulong
(constant) ulong io_uring_cqe_mixed.main.cookieNarrow = 1447446LU
cookieNarrow
= 0x16_16_16; // tag for the plain (16-byte) CQE
enum ulong
(constant) ulong io_uring_cqe_mixed.main.cookieWide = 3289650LU
cookieWide
= 0x32_32_32; // tag for the wide (32-byte) CQE
(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
;
// The only new ingredient vs. the NOP "hello world": the CQE_MIXED setup // flag. On Linux < 6.18 the kernel rejects this flag with -EINVAL; we treat // that (and a total lack of io_uring) as a clean SKIP. 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,
(enum) during.io_uring.SetupFlags

io_uring_setup() flags

SetupFlags
.
(enum value) during.io_uring.SetupFlags.CQE_MIXED = 262144u

IORING_SETUP_CQE_MIXED (from Linux 6.18)

Allow a mix of 16- and 32-byte CQEs in the same ring (per-CQE CQEFlags.F_32 selects).

CQE_MIXED
);
if (
(local variable) const(int) setupRet
setupRet
< 0)
{ if (
(local variable) const(int) setupRet
setupRet
== -
(constant) int core.stdc.errno.EINVAL = 22
EINVAL
)
void std.stdio.writefln!(char, const(int))(in char[] fmt, const(int) __param_1) @safe

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

writefln
("SKIP: IORING_SETUP_CQE_MIXED unsupported (kernel < 6.18) — errno %d", -
(local variable) const(int) setupRet
setupRet
);
else
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; } // SQE #1: a plain NOP. With nothing special requested it completes into a // normal 16-byte CQE — no F_32 tag.
(local variable) during.Uring io
io
.putWith!((ref SubmissionEntry e) {
e.prepNop(); e.user_data = cookieNarrow; })(); // SQE #2: a NOP that opts into a wide CQE. Setting IORING_NOP_CQE32 in the // op-specific `nop_flags` is what makes *this one* completion 32 bytes wide; // every other CQE on the ring stays 16 bytes. That per-SQE choice is exactly // what CQE_MIXED unlocks.
(local variable) during.Uring io
io
.putWith!((ref SubmissionEntry e) {
e.prepNop(); e.nop_flags = NOP_WANT_CQE32; e.user_data = cookieWide; })(); 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
(2);
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; } // Reap both completions. We don't rely on ordering: we match each CQE by its // user_data cookie and record whether the kernel marked it as a wide (F_32) // CQE. A NOP that should plainly succeed returning an error is a real bug. bool
(local variable) bool sawNarrow
sawNarrow
,
(local variable) bool sawWide
sawWide
;
bool
(local variable) bool narrowFlaggedWide
narrowFlaggedWide
,
(local variable) bool wideFlaggedWide
wideFlaggedWide
;
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
;
const
(local variable) const(bool) isWide
isWide
= (
(local variable) const(during.io_uring.CompletionEntry) cqe
cqe
.
(field) during.io_uring.CQEFlags during.io_uring.CompletionEntry.flags
flags
&
(enum) during.io_uring.CQEFlags

Flags used with CompletionEntry

CQEFlags
.
(enum value) during.io_uring.CQEFlags.F_32 = 32768u

IORING_CQE_F_32 (from Linux 6.18) Marks a 32-byte CQE in a ring configured with SetupFlags.CQE_MIXED.

F_32
) != 0;
if (
(local variable) const(during.io_uring.CompletionEntry) cqe
cqe
.
(field) int during.io_uring.CompletionEntry.res

result code for this event

res
< 0)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("NOP (user_data 0x%X) completed with error: errno %d",
(local variable) const(during.io_uring.CompletionEntry) cqe
cqe
.
(field) ulong during.io_uring.CompletionEntry.user_data

sqe->data submission passed back

user_data
, -
(local variable) const(during.io_uring.CompletionEntry) cqe
cqe
.
(field) int during.io_uring.CompletionEntry.res

result code for this event

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

Move to next CompletionEntry

popFront
();
return 1; } 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_cqe_mixed.main.cookieNarrow = 1447446LU
cookieNarrow
)
{
(local variable) bool sawNarrow
sawNarrow
= true;
(local variable) bool narrowFlaggedWide
narrowFlaggedWide
=
(local variable) const(bool) isWide
isWide
;
} else 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_cqe_mixed.main.cookieWide = 3289650LU
cookieWide
)
{
(local variable) bool sawWide
sawWide
= true;
(local variable) bool wideFlaggedWide
wideFlaggedWide
=
(local variable) const(bool) isWide
isWide
;
} else { stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("unexpected user_data on CQE: 0x%X",
(local variable) const(during.io_uring.CompletionEntry) cqe
cqe
.
(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
();
return 1; }
(local variable) during.Uring io
io
.
void during.Uring.popFront() pure nothrow @nogc @safe

Move to next CompletionEntry

popFront
();
} if (!
(local variable) bool sawNarrow
sawNarrow
|| !
(local variable) bool sawWide
sawWide
)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("missing completion(s): sawNarrow=%s sawWide=%s",
(local variable) bool sawNarrow
sawNarrow
,
(local variable) bool sawWide
sawWide
);
return 1; } // The plain NOP must NOT be flagged wide; the opted-in NOP MUST be — that // contrast is the proof that the ring really mixed CQE sizes. (Some kernels // may legitimately decline to widen a NOP that carries no extra payload; if // the wide CQE came back narrow we report that rather than failing, since // the mixed-ring setup itself — the 6.18 feature — already succeeded.) if (
(local variable) bool narrowFlaggedWide
narrowFlaggedWide
)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("plain NOP unexpectedly tagged F_32 (32-byte CQE)");
return 1; } if (
(local variable) bool wideFlaggedWide
wideFlaggedWide
)
void std.stdio.writefln!(char, ulong, ulong)(in char[] fmt, ulong __param_1, ulong __param_2) @safe

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

writefln
("ok: CQE_MIXED ring reaped a 16-byte CQE (data 0x%X) and a 32-byte CQE "
~ "(data 0x%X, F_32 set) from one ring — no ring-wide CQE32 doubling needed",
(constant) ulong io_uring_cqe_mixed.main.cookieNarrow = 1447446LU
cookieNarrow
,
(constant) ulong io_uring_cqe_mixed.main.cookieWide = 3289650LU
cookieWide
);
else
void std.stdio.writefln!char(in char[] fmt) @safe

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

writefln
("ok: CQE_MIXED ring set up and both NOPs completed (the kernel kept the "
~ "opted-in NOP at 16 bytes; the 6.18 mixed-CQE ring itself works)"); return 0; }