#!/usr/bin/env dub
/+ dub.sdl:
name "io_uring_nop"
dependency "during" version="~>0.5.0"
platforms "linux"
targetPath "build"
+/
/**
* `io_uring` — the minimal submit/complete cycle (`IORING_OP_NOP`, Linux 5.1).
*
* This is the "hello world" of `io_uring`: it sets up a ring, places one
* `NOP` submission queue entry (SQE), submits it, waits for the matching
* completion queue entry (CQE), and verifies the `user_data` cookie round-trips.
* It exercises every part of the core machinery from the 5.1 introduction —
* `io_uring_setup` (here via `Uring.setup`), the shared SQ/CQ rings, and the
* `submit` / `wait` / `front` / `popFront` flow that every later op reuses.
*
* Companion to the io_uring chronology:
* see docs/research/async-io/io-uring/timeline.md § "5.1 — The introduction".
*
* Run with: `dub run --single nop.d`
*
* Portability: if the running kernel has no `io_uring` (too old, or blocked by a
* seccomp/container policy), the program prints a `SKIP:` line and exits 0 so it
* stays green in CI regardless of the host kernel.
*/
module (module) io_uring_nopio_uring — the minimal submit/complete cycle (IORING_OP_NOP, Linux 5.1).
This is the "hello world" of io_uring: it sets up a ring, places one
NOP submission queue entry (SQE), submits it, waits for the matching
completion queue entry (CQE), and verifies the user_data cookie round-trips.
It exercises every part of the core machinery from the 5.1 introduction —
io_uring_setup (here via Uring.setup), the shared SQ/CQ rings, and the
submit / wait / front / popFront flow that every later op reuses.
Companion to the io_uring chronology:
see docs/research/async-io/io-uring/timeline.md § "5.1 — The introduction".
Run with: dub run --single nop.d
Portability
if the running kernel has no io_uring (too old, or blocked by a
seccomp/container policy), the program prints a SKIP: line and exits 0 so it
stays green in CI regardless of the host kernel.
io_uring_nop;
import (module) duringSimple idiomatic dlang wrapper around linux io_uring
(see: https://kernel.dk/io_uring.pdf) asynchronous API.
during;
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 : (alias template) io_uring_nop.writefln = std.stdio.writefln(alias fmt, A...)(A args) if (isSomeString!(typeof(fmt)))Equivalent to writef(fmt, args, '\n').
writefln, stderr;
int int D main()main()
{
// `IORING_OP_NOP` carries this cookie through the kernel and hands it back
// on the CQE — the mechanism every op uses to correlate completions.
enum ulong (constant) ulong io_uring_nop.main.cookie = 12648430LUcookie = 0xC0FFEE;
(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;
}
// Place a single NOP SQE. `putWith` clears the entry, runs the prep callback,
// and advances the submission queue tail in one step.
(local variable) during.Uring ioio.putWith!((ref SubmissionEntry e) {
e.prepNop();
e.user_data = cookie;
})();
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(1);
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;
}
// Block until at least one completion is ready, then consume it.
(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(int) resres = (local variable) during.Uring ioio.during.io_uring.CompletionEntry during.Uring.front() pure nothrow @nogc return ref @safeGet first CompletionEntry from cq ring
front.(field) int during.io_uring.CompletionEntry.resresult code for this event
res;
const (local variable) const(ulong) echoedechoed = (local variable) during.Uring ioio.during.io_uring.CompletionEntry during.Uring.front() pure nothrow @nogc return ref @safeGet first CompletionEntry from cq ring
front.(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(int) resres < 0)
{
stderr.std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @systemwritefln("NOP completed with error: errno %d", -(local variable) const(int) resres);
return 1;
}
if ((local variable) const(ulong) echoedechoed != (constant) ulong io_uring_nop.main.cookie = 12648430LUcookie)
{
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_nop.main.cookie = 12648430LUcookie, (local variable) const(ulong) echoedechoed);
return 1;
}
void std.stdio.writefln!(char, const(int), const(ulong))(in char[] fmt, const(int) __param_1, const(ulong) __param_2) @safeEquivalent to writef(fmt, args, '\n').
writefln("ok: NOP completed (res=%d), user_data 0x%X round-tripped through the ring", (local variable) const(int) resres, (local variable) const(ulong) echoedechoed);
return 0;
}