#!/usr/bin/env dub
/+ dub.sdl:
name "io_uring_openat_statx_close"
dependency "during" version="~>0.5.0"
platforms "linux"
targetPath "build"
+/
/**
* `io_uring` as a general async-syscall surface — `OPENAT` + `STATX` + `READ` +
* `CLOSE` (Linux 5.6).
*
* Linux 5.6 turned `io_uring` from an I/O-on-already-open-fds engine into a
* general asynchronous syscall surface: filesystem operations that previously
* had no async form — opening a path, stat-ing it, closing an fd — became plain
* SQE opcodes. This example chains the four of them to read a file end to end
* without ever issuing a synchronous open/stat/read/close:
*
* 1. write a known file under `/tmp` synchronously (libc) to have something to open;
* 2. `OPENAT(AT_FDCWD, path, O_RDONLY)` — the completion's `res` is the new fd;
* 3. `STATX(fd, "", AT_EMPTY_PATH, STATX_SIZE)` — verify the file size;
* 4. `READ(fd, buf, 0)` — verify the bytes round-trip;
* 5. `CLOSE(fd)` — release it through the ring.
*
* Each step is its own submit/wait: `OPENAT`'s result fd is the input to the
* following ops, so they cannot be batched into one independent submission.
*
* Companion to the io_uring chronology:
* see docs/research/async-io/io-uring/timeline.md
* § "5.6 — The filesystem/syscall expansion (March 2020)".
*
* Run with: `dub run --single openat-statx-close.d`
*
* Portability: prints a `SKIP:` line and exits 0 when `io_uring` is unavailable
* (old kernel / sandbox) or when any of these 5.6 ops is unsupported, so it stays
* green in CI regardless of the host kernel.
*/
module (module) io_uring_openat_statx_closeio_uring as a general async-syscall surface — OPENAT + STATX + READ +
CLOSE (Linux 5.6).
Linux 5.6 turned io_uring from an I/O-on-already-open-fds engine into a
general asynchronous syscall surface: filesystem operations that previously
had no async form — opening a path, stat-ing it, closing an fd — became plain
SQE opcodes. This example chains the four of them to read a file end to end
without ever issuing a synchronous open/stat/read/close:
write a known file under /tmp synchronously (libc) to have something to open;
OPENAT(AT_FDCWD, path, O_RDONLY) — the completion's res is the new fd;
STATX(fd, "", AT_EMPTY_PATH, STATX_SIZE) — verify the file size;
READ(fd, buf, 0) — verify the bytes round-trip;
CLOSE(fd) — release it through the ring.
Each step is its own submit/wait: OPENAT's result fd is the input to the
following ops, so they cannot be batched into one independent submission.
Companion to the io_uring chronology:
see docs/research/async-io/io-uring/timeline.md
§ "5.6 — The filesystem/syscall expansion (March 2020)".
Run with: dub run --single openat-statx-close.d
Portability
prints a SKIP: line and exits 0 when io_uring is unavailable
(old kernel / sandbox) or when any of these 5.6 ops is unsupported, so it stays
green in CI regardless of the host kernel.
io_uring_openat_statx_close;
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.syssys.(package) core.sys.posixposix.(module) core.sys.posix.fcntlD header file for POSIX.
fcntl : (alias constant) io_uring_openat_statx_close.AT_FDCWD = int core.sys.posix.fcntl.AT_FDCWD = -100AT_FDCWD, (alias constant) io_uring_openat_statx_close.O_CREAT = int core.sys.posix.fcntl.O_CREAT = 64O_CREAT, (alias constant) io_uring_openat_statx_close.O_RDONLY = int core.sys.posix.fcntl.O_RDONLY = 0O_RDONLY, (alias constant) io_uring_openat_statx_close.O_WRONLY = int core.sys.posix.fcntl.O_WRONLY = 1O_WRONLY, open;
import (package) corecore.(package) core.syssys.(package) core.sys.posixposix.(module) core.sys.posix.unistdD header file for POSIX.
unistd : (alias) io_uring_openat_statx_close.close = int core.sys.posix.unistd.close(int) nothrow @nogc @trustedclose, (alias) io_uring_openat_statx_close.unlink = int core.sys.posix.unistd.unlink(scope const(char*)) nothrow @nogcunlink, (alias) io_uring_openat_statx_close.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_openat_statx_close.writefln = std.stdio.writefln(alias fmt, A...)(A args) if (isSomeString!(typeof(fmt)))Equivalent to writef(fmt, args, '\n').
writefln;
import (package) stdstd.(module) std.stringString handling functions.
Category Functions Searching column indexOf indexOfAny indexOfNeither lastIndexOf lastIndexOfAny lastIndexOfNeither Comparison isNumeric Mutation capitalize Pruning and Filling center chomp chompPrefix chop detabber detab entab entabber leftJustify outdent rightJustify strip stripLeft stripRight wrap Substitution abbrev soundex soundexer succ tr translate Miscellaneous assumeUTF fromStringz lineSplitter representation splitLines toStringz Objects of types string, wstring, and dstring are value types and cannot be mutated element-by-element. For using mutation during building strings, use char[], wchar[], or dchar[]. The xxxstring types are preferable because they don't exhibit undesired aliasing, thus making code more robust.
The following functions are publicly imported:
Module Functions Publicly imported functions std.algorithm cmp, std,algorithm,comparison count, std,algorithm,searching endsWith, std,algorithm,searching startsWith, std,algorithm,searching std.array join, std,array replace, std,array replaceInPlace, std,array split, std,array empty, std,array std.format format, std,format sformat, std,format std.uni icmp, std,uni toLower, std,uni toLowerInPlace, std,uni toUpper, std,uni toUpperInPlace, std,uni There is a rich set of functions for string handling defined in other modules. Functions related to Unicode and ASCII are found in std.uni and std.ascii, respectively. Other functions that have a wider generality than just strings can be found in std.algorithm and std.range.
Source
std/string.d
string : (alias) io_uring_openat_statx_close.toStringz = immutable(char)* std.string.toStringz(scope const(char)[] s) pure nothrow @trustedtoStringz;
// ABI-stable constants the kernel uses but druntime does not surface here.
enum int (constant) int io_uring_openat_statx_close.AT_EMPTY_PATH = 4096AT_EMPTY_PATH = 0x1000; // statx() on an open fd with an empty path
enum uint (constant) uint io_uring_openat_statx_close.STATX_SIZE = 512uSTATX_SIZE = 0x0000_0200; // request stx_size in the result mask
// Minimal mirror of the kernel `struct statx` (256 bytes, ABI-stable). We only
// read `stx_size`, which lives at byte offset 40; the rest is padding we never
// touch. `prepStatx` takes the buffer generically, so any 256-byte struct with
// the field at the right offset works.
struct (struct) io_uring_openat_statx_close.StatxStatx
{
uint (field) uint io_uring_openat_statx_close.Statx.stx_maskstx_mask; // 0
uint (field) uint io_uring_openat_statx_close.Statx.stx_blksizestx_blksize; // 4
ulong (field) ulong io_uring_openat_statx_close.Statx.stx_attributesstx_attributes; // 8
uint (field) uint io_uring_openat_statx_close.Statx.stx_nlinkstx_nlink; // 16
uint (field) uint io_uring_openat_statx_close.Statx.stx_uidstx_uid; // 20
uint (field) uint io_uring_openat_statx_close.Statx.stx_gidstx_gid; // 24
ushort (field) ushort io_uring_openat_statx_close.Statx.stx_modestx_mode; // 28
ushort[1] (field) ushort[1] io_uring_openat_statx_close.Statx._spare0_spare0; // 30
ulong (field) ulong io_uring_openat_statx_close.Statx.stx_inostx_ino; // 32
ulong (field) ulong io_uring_openat_statx_close.Statx.stx_sizestx_size; // 40 -- the only field we assert on
ubyte[256 - 48] (field) ubyte[208] io_uring_openat_statx_close.Statx._rest_rest; // 48.. -- timestamps, dev numbers, future fields
}
static assert((struct) io_uring_openat_statx_close.StatxStatx.(field) ulong io_uring_openat_statx_close.Statx.stx_sizestx_size.offsetof == 40, "stx_size must sit at the kernel ABI offset");
static assert((struct) io_uring_openat_statx_close.StatxStatx.sizeof >= 256, "statx buffer must cover the full kernel struct");
int int D main()main()
{
// The file we will open/stat/read through the ring.
enum (alias) object.string = stringstring (constant) string io_uring_openat_statx_close.main.path = "/tmp/io_uring_openat_statx_close.txt"path = "/tmp/io_uring_openat_statx_close.txt";
immutable(ubyte)[] (local variable) immutable(ubyte)[] payloadpayload = cast(immutable(ubyte)[]) "io_uring touched the filesystem\n";
// --- Step 1: create the test file synchronously (plain libc). -----------
{
const (local variable) const(int) wfdwfd = open(int core.sys.posix.fcntl.open64(scope const(char*), int, ...) nothrow @nogcpath.immutable(char)* std.string.toStringz(scope const(char)[] s) pure nothrow @trustedExamples
import core.stdc.string : strlen;
import std.conv : to;
auto p = toStringz("foo");
assert(strlen(p) == 3);
const(char)[] foo = "abbzxyzzy";
p = toStringz(foo[3 .. 5]);
assert(strlen(p) == 2);
string test = "";
p = toStringz(test);
assert(*p == 0);
test = "\0";
p = toStringz(test);
assert(*p == 0);
test = "foo\0";
p = toStringz(test);
assert(p[0] == 'f' && p[1] == 'o' && p[2] == 'o' && p[3] == 0);
const string test2 = "";
p = toStringz(test2);
assert(*p == 0);
assert(toStringz([]) is toStringz(""));
toStringz, (constant) int core.sys.posix.fcntl.O_CREAT = 64O_CREAT | (constant) int core.sys.posix.fcntl.O_WRONLY = 1O_WRONLY, (template instance) io_uring_openat_statx_close.octal!"600"octal!"600");
if ((local variable) const(int) wfdwfd < 0)
{
void std.stdio.writefln!(char, string, int)(in char[] fmt, string __param_1, int __param_2) @safeEquivalent to writef(fmt, args, '\n').
writefln("SKIP: could not create %s (errno %d)", (constant) string io_uring_openat_statx_close.main.path = "/tmp/io_uring_openat_statx_close.txt"path, int io_uring_openat_statx_close.errnoOf(int ret) pure nothrow @nogc @safeerrnoOf((local variable) const(int) wfdwfd));
return 0;
}
const (local variable) const(long) wrotewrote = long core.sys.posix.unistd.write(int, scope const(void*), ulong) nothrow @nogcwrite((local variable) const(int) wfdwfd, (local variable) immutable(ubyte)[] payloadpayload.(field) immutable(ubyte)* immutable(ubyte)[].ptrptr, (local variable) immutable(ubyte)[] payloadpayload.(field) ulong immutable(ubyte)[].lengthlength);
int core.sys.posix.unistd.close(int) nothrow @nogc @trustedclose((local variable) const(int) wfdwfd);
if ((local variable) const(long) wrotewrote != (local variable) immutable(ubyte)[] payloadpayload.(field) ulong immutable(ubyte)[].lengthlength)
{
stderr.std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @systemwritefln("setup write short: %d of %d bytes", (local variable) const(long) wrotewrote, (local variable) immutable(ubyte)[] payloadpayload.(field) ulong immutable(ubyte)[].lengthlength);
return 1;
}
}
scope (exit) int core.sys.posix.unistd.unlink(scope const(char*)) nothrow @nogcunlink((constant) string io_uring_openat_statx_close.main.path = "/tmp/io_uring_openat_statx_close.txt"path.immutable(char)* std.string.toStringz(scope const(char)[] s) pure nothrow @trustedExamples
import core.stdc.string : strlen;
import std.conv : to;
auto p = toStringz("foo");
assert(strlen(p) == 3);
const(char)[] foo = "abbzxyzzy";
p = toStringz(foo[3 .. 5]);
assert(strlen(p) == 2);
string test = "";
p = toStringz(test);
assert(*p == 0);
test = "\0";
p = toStringz(test);
assert(*p == 0);
test = "foo\0";
p = toStringz(test);
assert(p[0] == 'f' && p[1] == 'o' && p[2] == 'o' && p[3] == 0);
const string test2 = "";
p = toStringz(test2);
assert(*p == 0);
assert(toStringz([]) is toStringz(""));
toStringz);
// --- Ring setup. --------------------------------------------------------
(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;
}
// The filesystem ops all arrived together in 5.6; probe one representative
// op up front so a pre-5.6 kernel skips cleanly rather than erroring mid-chain.
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.OPENAT = cast(ubyte)18uIORING_OP_OPENAT
OPENAT))
{
void std.stdio.writefln!char(in char[] fmt) @safeEquivalent to writef(fmt, args, '\n').
writefln("SKIP: IORING_OP_OPENAT unsupported on this kernel (pre-5.6)");
return 0;
}
// --- Step 2: OPENAT — the completion res is the freshly opened fd. ------
auto (local variable) immutable(char)* cpathcpath = (constant) string io_uring_openat_statx_close.main.path = "/tmp/io_uring_openat_statx_close.txt"path.immutable(char)* std.string.toStringz(scope const(char)[] s) pure nothrow @trustedExamples
import core.stdc.string : strlen;
import std.conv : to;
auto p = toStringz("foo");
assert(strlen(p) == 3);
const(char)[] foo = "abbzxyzzy";
p = toStringz(foo[3 .. 5]);
assert(strlen(p) == 2);
string test = "";
p = toStringz(test);
assert(*p == 0);
test = "\0";
p = toStringz(test);
assert(*p == 0);
test = "foo\0";
p = toStringz(test);
assert(p[0] == 'f' && p[1] == 'o' && p[2] == 'o' && p[3] == 0);
const string test2 = "";
p = toStringz(test2);
assert(*p == 0);
assert(toStringz([]) is toStringz(""));
toStringz; // keep the C string alive across the submit
(local variable) during.Uring ioio.putWith!((ref SubmissionEntry e, const(char)* p) {
e.prepOpenat(AT_FDCWD, p, O_RDONLY, 0);
e.user_data = 1;
})(during.Uring during.Uring.putWith!(function (ref during.io_uring.SubmissionEntry e, const(char)* p) nothrow @nogc @system
{
prepOpenat(e, -100, p, 0, 0u);
e.user_data = 1LU;
}
, immutable(char)*)(ref immutable(char)* __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().
cpath);
if ((local variable) during.Uring ioio.int during.Uring.submit(uint want) nothrow @nogc @safeSubmits qued SubmissionEntry to be processed by kernel.
submit(1) < 0)
return int io_uring_openat_statx_close.fail(string what)fail("submit OPENAT");
(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) openResopenRes = (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;
(local variable) during.Uring ioio.void during.Uring.popFront() pure nothrow @nogc @safeMove to next CompletionEntry
popFront();
if (bool io_uring_openat_statx_close.isUnsupported(int res) pure nothrow @nogc @safeisUnsupported((local variable) const(int) openResopenRes))
{
void std.stdio.writefln!(char, const(int))(in char[] fmt, const(int) __param_1) @safeEquivalent to writef(fmt, args, '\n').
writefln("SKIP: OPENAT returned %d (op unsupported on this kernel)", (local variable) const(int) openResopenRes);
return 0;
}
if ((local variable) const(int) openResopenRes < 0)
return int io_uring_openat_statx_close.failErr(string op, int res)failErr("OPENAT", (local variable) const(int) openResopenRes);
const (local variable) const(int) fdfd = (local variable) const(int) openResopenRes; // the async-opened fd, used by the next ops
// From here a failure leaks the fd unless we close it; do that on any early return.
scope (failure) int core.sys.posix.unistd.close(int) nothrow @nogc @trustedclose((local variable) const(int) fdfd);
// --- Step 3: STATX on the open fd (empty path + AT_EMPTY_PATH). ---------
(struct) io_uring_openat_statx_close.StatxStatx (local variable) io_uring_openat_statx_close.Statx stxstx;
(local variable) during.Uring ioio.putWith!((ref SubmissionEntry e, int f, Statx* sb) {
// Empty path + AT_EMPTY_PATH means "stat the fd itself", like fstat().
e.prepStatx(f, emptyCString, AT_EMPTY_PATH, STATX_SIZE, *sb);
e.user_data = 2;
})(during.Uring during.Uring.putWith!(function (ref during.io_uring.SubmissionEntry e, int f, io_uring_openat_statx_close.Statx* sb) nothrow @nogc @system
{
prepStatx(e, f, emptyCString(), 4096, 512u, *sb);
e.user_data = 2LU;
}
, const(int), io_uring_openat_statx_close.Statx*)(ref const(int) __param_0, io_uring_openat_statx_close.Statx* __param_1) 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().
fd, &during.Uring during.Uring.putWith!(function (ref during.io_uring.SubmissionEntry e, int f, io_uring_openat_statx_close.Statx* sb) nothrow @nogc @system
{
prepStatx(e, f, emptyCString(), 4096, 512u, *sb);
e.user_data = 2LU;
}
, const(int), io_uring_openat_statx_close.Statx*)(ref const(int) __param_0, io_uring_openat_statx_close.Statx* __param_1) 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().
stx);
if ((local variable) during.Uring ioio.int during.Uring.submit(uint want) nothrow @nogc @safeSubmits qued SubmissionEntry to be processed by kernel.
submit(1) < 0)
return int io_uring_openat_statx_close.failClose(int fd, string what)failClose((local variable) const(int) fdfd, "submit STATX");
(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) statxResstatxRes = (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;
(local variable) during.Uring ioio.void during.Uring.popFront() pure nothrow @nogc @safeMove to next CompletionEntry
popFront();
if (bool io_uring_openat_statx_close.isUnsupported(int res) pure nothrow @nogc @safeisUnsupported((local variable) const(int) statxResstatxRes))
{
int core.sys.posix.unistd.close(int) nothrow @nogc @trustedclose((local variable) const(int) fdfd);
void std.stdio.writefln!(char, const(int))(in char[] fmt, const(int) __param_1) @safeEquivalent to writef(fmt, args, '\n').
writefln("SKIP: STATX returned %d (op unsupported on this kernel)", (local variable) const(int) statxResstatxRes);
return 0;
}
if ((local variable) const(int) statxResstatxRes < 0)
{
int core.sys.posix.unistd.close(int) nothrow @nogc @trustedclose((local variable) const(int) fdfd);
return int io_uring_openat_statx_close.failErr(string op, int res)failErr("STATX", (local variable) const(int) statxResstatxRes);
}
if ((local variable) io_uring_openat_statx_close.Statx stxstx.(field) ulong io_uring_openat_statx_close.Statx.stx_sizestx_size != (local variable) immutable(ubyte)[] payloadpayload.(field) ulong immutable(ubyte)[].lengthlength)
{
int core.sys.posix.unistd.close(int) nothrow @nogc @trustedclose((local variable) const(int) fdfd);
stderr.std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @systemwritefln("STATX size mismatch: expected %d, got %d", (local variable) immutable(ubyte)[] payloadpayload.(field) ulong immutable(ubyte)[].lengthlength, (local variable) io_uring_openat_statx_close.Statx stxstx.(field) ulong io_uring_openat_statx_close.Statx.stx_sizestx_size);
return 1;
}
// --- Step 4: READ the whole file through the ring and verify content. ---
ubyte[128] (local variable) ubyte[128] readBufreadBuf;
(local variable) during.Uring ioio.putWith!((ref SubmissionEntry e, int f, ubyte[] b) {
e.prepRead(f, b, 0);
e.user_data = 3;
})(during.Uring during.Uring.putWith!(function (ref during.io_uring.SubmissionEntry e, int f, ubyte[] b) nothrow @nogc @safe
{
prepRead(e, f, b, 0L);
e.user_data = 3LU;
}
, const(int), ubyte[])(ref const(int) __param_0, ubyte[] __param_1) 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().
fd, during.Uring during.Uring.putWith!(function (ref during.io_uring.SubmissionEntry e, int f, ubyte[] b) nothrow @nogc @safe
{
prepRead(e, f, b, 0L);
e.user_data = 3LU;
}
, const(int), ubyte[])(ref const(int) __param_0, ubyte[] __param_1) 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().
readBuf[]);
if ((local variable) during.Uring ioio.int during.Uring.submit(uint want) nothrow @nogc @safeSubmits qued SubmissionEntry to be processed by kernel.
submit(1) < 0)
return int io_uring_openat_statx_close.failClose(int fd, string what)failClose((local variable) const(int) fdfd, "submit READ");
(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) readResreadRes = (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;
(local variable) during.Uring ioio.void during.Uring.popFront() pure nothrow @nogc @safeMove to next CompletionEntry
popFront();
if ((local variable) const(int) readResreadRes < 0)
{
int core.sys.posix.unistd.close(int) nothrow @nogc @trustedclose((local variable) const(int) fdfd);
return int io_uring_openat_statx_close.failErr(string op, int res)failErr("READ", (local variable) const(int) readResreadRes);
}
if ((local variable) const(int) readResreadRes != cast(int) (local variable) immutable(ubyte)[] payloadpayload.(field) ulong immutable(ubyte)[].lengthlength || (local variable) ubyte[128] readBufreadBuf[0 .. (local variable) immutable(ubyte)[] payloadpayload.(field) ulong immutable(ubyte)[].lengthlength] != (local variable) immutable(ubyte)[] payloadpayload[])
{
int core.sys.posix.unistd.close(int) nothrow @nogc @trustedclose((local variable) const(int) fdfd);
stderr.std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @systemwritefln("READ content mismatch (res=%d)", (local variable) const(int) readResreadRes);
return 1;
}
// --- Step 5: CLOSE the fd through the ring (no scope guard hereafter). ---
(local variable) during.Uring ioio.putWith!((ref SubmissionEntry e, int f) {
e.prepClose(f);
e.user_data = 4;
})(during.Uring during.Uring.putWith!(function (ref during.io_uring.SubmissionEntry e, int f) nothrow @nogc @safe
{
prepClose(e, f);
e.user_data = 4LU;
}
, const(int))(ref const(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().
fd);
if ((local variable) during.Uring ioio.int during.Uring.submit(uint want) nothrow @nogc @safeSubmits qued SubmissionEntry to be processed by kernel.
submit(1) < 0)
return int io_uring_openat_statx_close.failClose(int fd, string what)failClose((local variable) const(int) fdfd, "submit CLOSE");
(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) closeRescloseRes = (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;
(local variable) during.Uring ioio.void during.Uring.popFront() pure nothrow @nogc @safeMove to next CompletionEntry
popFront();
if ((local variable) const(int) closeRescloseRes < 0)
{
int core.sys.posix.unistd.close(int) nothrow @nogc @trustedclose((local variable) const(int) fdfd); // ring CLOSE failed; fall back to a synchronous close
return int io_uring_openat_statx_close.failErr(string op, int res)failErr("CLOSE", (local variable) const(int) closeRescloseRes);
}
void std.stdio.writefln!(char, ulong, const(int))(in char[] fmt, ulong __param_1, const(int) __param_2) @safeEquivalent to writef(fmt, args, '\n').
writefln("ok: async OPENAT→STATX(size=%d)→READ(%d bytes)→CLOSE chained through io_uring",
(local variable) io_uring_openat_statx_close.Statx stxstx.(field) ulong io_uring_openat_statx_close.Statx.stx_sizestx_size, (local variable) const(int) readResreadRes);
return 0;
}
// --- small helpers ----------------------------------------------------------
// A `const(char)*` to a single NUL byte: an empty C path for STATX-on-fd.
const(char)* const(char)* io_uring_openat_statx_close.emptyCString() nothrow @nogc @trustedemptyCString() @trusted nothrow @nogc
{
static immutable char[1] (immutable global) immutable(char[1]) io_uring_openat_statx_close.emptyCString.emptyempty = ['\0'];
return &(immutable global) immutable(char[1]) io_uring_openat_statx_close.emptyCString.emptyempty[0];
}
// libc syscalls return -1 and set errno; this wrapper environment doesn't read
// errno portably, so for the setup path we just report the raw -1.
int int io_uring_openat_statx_close.errnoOf(int ret) pure nothrow @nogc @safeerrnoOf(int (parameter) int retret) @safe pure nothrow @nogc => -(parameter) int retret;
// io_uring surfaces "this op doesn't exist on this kernel" as -EINVAL/-EOPNOTSUPP/-ENOSYS.
bool bool io_uring_openat_statx_close.isUnsupported(int res) pure nothrow @nogc @safeisUnsupported(int (parameter) int resres) @safe pure nothrow @nogc
{
enum int (constant) int io_uring_openat_statx_close.isUnsupported.EINVAL = 22EINVAL = 22, (constant) int io_uring_openat_statx_close.isUnsupported.ENOSYS = 38ENOSYS = 38, (constant) int io_uring_openat_statx_close.isUnsupported.EOPNOTSUPP = 95EOPNOTSUPP = 95;
return (parameter) int resres == -(constant) int io_uring_openat_statx_close.isUnsupported.EINVAL = 22EINVAL || (parameter) int resres == -(constant) int io_uring_openat_statx_close.isUnsupported.EOPNOTSUPP = 95EOPNOTSUPP || (parameter) int resres == -(constant) int io_uring_openat_statx_close.isUnsupported.ENOSYS = 38ENOSYS;
}
int int io_uring_openat_statx_close.fail(string what)fail((alias) object.string = stringstring (parameter) string whatwhat)
{
stderr.std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @systemwritefln("%s failed", (parameter) string whatwhat);
return 1;
}
int int io_uring_openat_statx_close.failErr(string op, int res)failErr((alias) object.string = stringstring (parameter) string opop, int (parameter) int resres)
{
stderr.std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @systemwritefln("%s completed with error: errno %d", (parameter) string opop, -(parameter) int resres);
return 1;
}
int int io_uring_openat_statx_close.failClose(int fd, string what)failClose(int (parameter) int fdfd, (alias) object.string = stringstring (parameter) string whatwhat)
{
int core.sys.posix.unistd.close(int) nothrow @nogc @trustedclose((parameter) int fdfd);
return int io_uring_openat_statx_close.fail(string what)fail((parameter) string whatwhat);
}
// `octal!"600"` — file mode literal computed at compile time.
template (template) io_uring_openat_statx_close.octal(string digits)octal(string digits)
{
enum uint (constant) uint io_uring_openat_statx_close.octal!"600" = 384uoctal = uint io_uring_openat_statx_close.parseOctal(string s) pure nothrow @nogc @safeparseOctal((constant) string io_uring_openat_statx_close.digits = "600"digits);
}
uint uint io_uring_openat_statx_close.parseOctal(string s) pure nothrow @nogc @safeparseOctal((alias) object.string = stringstring (parameter) string ss) @safe pure nothrow @nogc
{
uint (local variable) uint vv = 0;
foreach ((parameter) immutable(char) cc; (parameter) string ss)
(local variable) uint vv = (local variable) uint vv * 8 + ((local variable) immutable(char) cc - '0');
return (local variable) uint vv;
}