#!/usr/bin/env dub
/+ dub.sdl:
name "io_uring_tcp_echo"
dependency "during" version="~>0.5.0"
platforms "linux"
targetPath "build"
+/
/**
* `io_uring` networking foundation — `ACCEPT` + `CONNECT` + `SEND` + `RECV`
* over a single ring (Linux 5.5 for accept/connect, 5.6 for send/recv).
*
* Before 5.5, sockets had to be driven indirectly through `POLL_ADD` plus a
* separate `accept(2)`/`connect(2)` syscall (the `echo_server` fork example
* still polls). 5.5 added first-class `IORING_OP_ACCEPT` and
* `IORING_OP_CONNECT`; 5.6 added `IORING_OP_SEND` / `IORING_OP_RECV`. Together
* they let a TCP echo round-trip run entirely inside the ring.
*
* This program drives a one-shot loopback echo with a SINGLE ring, strictly
* sequentially:
* 1. libc creates a listening socket bound to 127.0.0.1:0 (SO_REUSEADDR,
* listen); `getsockname` recovers the kernel-assigned port.
* 2. libc creates a client socket.
* 3. CONNECT (client) + ACCEPT (listener) are submitted on the same ring and
* both completions are drained. ACCEPT yields the server-side accepted fd.
* 4. SEND a known payload on the client; RECV it on the accepted server fd.
* 5. Verify the received bytes match what was sent.
*
* Companion to the io_uring chronology:
* see docs/research/async-io/io-uring/timeline.md
* § "5.5 — Accept/connect, cancel, link-timeout (January 2020)".
*
* Run with: `dub run --single tcp-echo.d`
*
* Portability: if `io_uring` is unavailable, or this kernel lacks the
* accept/connect/send/recv ops (anything before ~5.5/5.6), the program prints
* a `SKIP:` line and exits 0 so it stays green in CI regardless of host kernel.
*/
module (module) io_uring_tcp_echoio_uring networking foundation — ACCEPT + CONNECT + SEND + RECV
over a single ring (Linux 5.5 for accept/connect, 5.6 for send/recv).
Before 5.5, sockets had to be driven indirectly through POLL_ADD plus a
separate accept(2)/connect(2) syscall (the echo_server fork example
still polls). 5.5 added first-class IORING_OP_ACCEPT and
IORING_OP_CONNECT; 5.6 added IORING_OP_SEND / IORING_OP_RECV. Together
they let a TCP echo round-trip run entirely inside the ring.
This program drives a one-shot loopback echo with a SINGLE ring, strictly
sequentially:
libc creates a listening socket bound to 127.0.0.1:0 (SO_REUSEADDR,
listen); getsockname recovers the kernel-assigned port.
libc creates a client socket.
CONNECT (client) + ACCEPT (listener) are submitted on the same ring and
both completions are drained. ACCEPT yields the server-side accepted fd.
SEND a known payload on the client; RECV it on the accepted server fd.
Verify the received bytes match what was sent.
Companion to the io_uring chronology:
see docs/research/async-io/io-uring/timeline.md
§ "5.5 — Accept/connect, cancel, link-timeout (January 2020)".
Run with: dub run --single tcp-echo.d
Portability
if io_uring is unavailable, or this kernel lacks the
accept/connect/send/recv ops (anything before ~5.5/5.6), the program prints
a SKIP: line and exits 0 so it stays green in CI regardless of host kernel.
io_uring_tcp_echo;
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_tcp_echo.writefln = std.stdio.writefln(alias fmt, A...)(A args) if (isSomeString!(typeof(fmt)))Equivalent to writef(fmt, args, '\n').
writefln, stderr;
import (package) corecore.(package) core.syssys.(package) core.sys.posixposix.(package) core.sys.posix.arpaarpa.(module) core.sys.posix.arpa.inetD header file for POSIX.
inet : (alias) io_uring_tcp_echo.htonl = uint core.sys.posix.arpa.inet.htonl(uint) pure nothrow @nogc @trustedhtonl, (alias) io_uring_tcp_echo.ntohs = ushort core.sys.posix.arpa.inet.ntohs(ushort) pure nothrow @nogc @trustedntohs;
import (package) corecore.(package) core.syssys.(package) core.sys.posixposix.(package) core.sys.posix.netinetnetinet.(module) core.sys.posix.netinet.in_D header file for POSIX.
in_;
import (package) corecore.(package) core.syssys.(package) core.sys.posixposix.(package) core.sys.posix.syssys.(module) core.sys.posix.sys.socketD header file for POSIX.
socket;
import (package) corecore.(package) core.syssys.(package) core.sys.posixposix.(module) core.sys.posix.unistdD header file for POSIX.
unistd : (alias) io_uring_tcp_echo.close = int core.sys.posix.unistd.close(int) nothrow @nogc @trustedclose;
// user_data cookies so each completion can be correlated to its op.
enum ulong (constant) ulong io_uring_tcp_echo.CONNECT_TAG = 1LUCONNECT_TAG = 1;
enum ulong (constant) ulong io_uring_tcp_echo.ACCEPT_TAG = 2LUACCEPT_TAG = 2;
enum ulong (constant) ulong io_uring_tcp_echo.SEND_TAG = 3LUSEND_TAG = 3;
enum ulong (constant) ulong io_uring_tcp_echo.RECV_TAG = 4LURECV_TAG = 4;
// Known payload echoed across loopback.
static immutable ubyte[] (immutable global) immutable(ubyte[]) io_uring_tcp_echo.PAYLOADPAYLOAD = cast(immutable ubyte[]) "io_uring echo \xF0\x9F\x9A\x80";
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;
}
// Probe for the socket ops. ACCEPT/CONNECT arrived in 5.5, SEND/RECV in
// 5.6; on an older kernel any of these will be missing, so we skip cleanly.
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)
{
foreach ((parameter) during.io_uring.Operation opop; [(enum) during.io_uring.OperationDescribes the operation to be performed
Operation.(enum value) during.io_uring.Operation.ACCEPT = cast(ubyte)13uIORING_OP_ACCEPT
ACCEPT, (enum) during.io_uring.OperationDescribes the operation to be performed
Operation.(enum value) during.io_uring.Operation.CONNECT = cast(ubyte)16uIORING_OP_CONNECT
CONNECT, (enum) during.io_uring.OperationDescribes the operation to be performed
Operation.(enum value) during.io_uring.Operation.SEND = cast(ubyte)26uIORING_OP_SEND
SEND, (enum) during.io_uring.OperationDescribes the operation to be performed
Operation.(enum value) during.io_uring.Operation.RECV = cast(ubyte)27uIORING_OP_RECV
RECV])
{
if (!(local variable) during.Probe probeprobe.bool during.Probe.isSupported(during.io_uring.Operation op) const pure nothrow @nogc @safeIs operation supported?
isSupported((local variable) during.io_uring.Operation opop))
{
void std.stdio.writefln!(char, int)(in char[] fmt, int __param_1) @safeEquivalent to writef(fmt, args, '\n').
writefln("SKIP: io_uring op %d unsupported on this kernel (needs Linux 5.5/5.6)",
cast(int) (local variable) during.io_uring.Operation opop);
return 0;
}
}
}
// --- 1. Listening socket on 127.0.0.1:0 (kernel picks the port) --------
immutable (local variable) immutable(int) srvsrv = int core.sys.posix.sys.socket.socket(int, int, int) nothrow @nogc @safesocket((enum value) core.sys.posix.sys.socket.AF_INET = 2AF_INET, (enum value) core.sys.posix.sys.socket.SOCK_STREAM = 1SOCK_STREAM, 0);
if ((local variable) immutable(int) srvsrv < 0)
{
stderr.std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @systemwritefln("socket(srv) failed");
return 1;
}
scope (exit) int core.sys.posix.unistd.close(int) nothrow @nogc @trustedclose((local variable) immutable(int) srvsrv);
int (local variable) int oneone = 1;
int core.sys.posix.sys.socket.setsockopt(int, int, int, scope const(void*), uint) nothrow @nogcsetsockopt((local variable) immutable(int) srvsrv, (enum value) core.sys.posix.sys.socket.SOL_SOCKET = 1SOL_SOCKET, (enum value) core.sys.posix.sys.socket.SO_REUSEADDR = 2SO_REUSEADDR, &(local variable) int oneone, (local variable) int oneone.(constant) ulong int.sizeof = 4LUsizeof);
(struct) core.sys.posix.netinet.in_.sockaddr_insockaddr_in (local variable) core.sys.posix.netinet.in_.sockaddr_in saddrsaddr;
(local variable) core.sys.posix.netinet.in_.sockaddr_in saddrsaddr.(field) ushort core.sys.posix.netinet.in_.sockaddr_in.sin_familysin_family = (enum value) core.sys.posix.sys.socket.AF_INET = 2AF_INET;
(local variable) core.sys.posix.netinet.in_.sockaddr_in saddrsaddr.(field) ushort core.sys.posix.netinet.in_.sockaddr_in.sin_portsin_port = 0; // kernel-assigned
(local variable) core.sys.posix.netinet.in_.sockaddr_in saddrsaddr.(field) core.sys.posix.arpa.inet.in_addr core.sys.posix.netinet.in_.sockaddr_in.sin_addrsin_addr.(field) uint core.sys.posix.arpa.inet.in_addr.s_addrs_addr = uint core.sys.posix.arpa.inet.htonl(uint) pure nothrow @nogc @trustedhtonl(0x7f00_0001); // 127.0.0.1
if (int core.sys.posix.sys.socket.bind(int, scope const(core.sys.posix.sys.socket.sockaddr*), uint) nothrow @nogcbind((local variable) immutable(int) srvsrv, cast((struct) core.sys.posix.sys.socket.sockaddrsockaddr*)&(local variable) core.sys.posix.netinet.in_.sockaddr_in saddrsaddr, (struct) core.sys.posix.netinet.in_.sockaddr_insockaddr_in.(constant) ulong core.sys.posix.netinet.in_.sockaddr_in.sizeof = 16LUsizeof) != 0)
{
stderr.std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @systemwritefln("bind() failed");
return 1;
}
if (int core.sys.posix.sys.socket.listen(int, int) nothrow @nogc @safelisten((local variable) immutable(int) srvsrv, 4) != 0)
{
stderr.std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @systemwritefln("listen() failed");
return 1;
}
// Recover the actual bound address (the port the kernel chose) — this is
// what the client CONNECTs to.
(struct) core.sys.posix.netinet.in_.sockaddr_insockaddr_in (local variable) core.sys.posix.netinet.in_.sockaddr_in boundbound;
(alias) core.sys.posix.sys.socket.socklen_t = uintsocklen_t (local variable) uint blenblen = (local variable) core.sys.posix.netinet.in_.sockaddr_in boundbound.(constant) ulong core.sys.posix.netinet.in_.sockaddr_in.sizeof = 16LUsizeof;
if (int core.sys.posix.sys.socket.getsockname(int, scope core.sys.posix.sys.socket.sockaddr*, scope uint*) nothrow @nogcgetsockname((local variable) immutable(int) srvsrv, cast((struct) core.sys.posix.sys.socket.sockaddrsockaddr*)&(local variable) core.sys.posix.netinet.in_.sockaddr_in boundbound, &(local variable) uint blenblen) != 0)
{
stderr.std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @systemwritefln("getsockname() failed");
return 1;
}
// --- 2. Client socket --------------------------------------------------
immutable (local variable) immutable(int) clicli = int core.sys.posix.sys.socket.socket(int, int, int) nothrow @nogc @safesocket((enum value) core.sys.posix.sys.socket.AF_INET = 2AF_INET, (enum value) core.sys.posix.sys.socket.SOCK_STREAM = 1SOCK_STREAM, 0);
if ((local variable) immutable(int) clicli < 0)
{
stderr.std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @systemwritefln("socket(cli) failed");
return 1;
}
scope (exit) int core.sys.posix.unistd.close(int) nothrow @nogc @trustedclose((local variable) immutable(int) clicli);
// --- 3. CONNECT (client) + ACCEPT (listener) on a single ring ----------
// ACCEPT writes the peer address + length into these out-params.
(struct) core.sys.posix.netinet.in_.sockaddr_insockaddr_in (local variable) core.sys.posix.netinet.in_.sockaddr_in peerpeer;
(alias) core.sys.posix.sys.socket.socklen_t = uintsocklen_t (local variable) uint plenplen = (local variable) core.sys.posix.netinet.in_.sockaddr_in peerpeer.(constant) ulong core.sys.posix.netinet.in_.sockaddr_in.sizeof = 16LUsizeof;
// CONNECT must reference an address that lives until the op completes;
// `bound` is a stack local that outlives the synchronous submit/wait below.
(local variable) during.Uring ioio.putWith!((ref SubmissionEntry e, int fd, sockaddr_in* a) {
e.prepConnect(fd, *a);
e.user_data = CONNECT_TAG;
})(during.Uring during.Uring.putWith!(function (ref during.io_uring.SubmissionEntry e, int fd, core.sys.posix.netinet.in_.sockaddr_in* a) nothrow @nogc @safe
{
prepConnect(e, fd, *a);
e.user_data = 1LU;
}
, immutable(int), core.sys.posix.netinet.in_.sockaddr_in*)(ref immutable(int) __param_0, core.sys.posix.netinet.in_.sockaddr_in* __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().
cli, &(local variable) core.sys.posix.netinet.in_.sockaddr_in boundbound);
(local variable) during.Uring ioio.putWith!((ref SubmissionEntry e, int fd, sockaddr_in* a, socklen_t* al) {
e.prepAccept(fd, *a, *al);
e.user_data = ACCEPT_TAG;
})(during.Uring during.Uring.putWith!(function (ref during.io_uring.SubmissionEntry e, int fd, core.sys.posix.netinet.in_.sockaddr_in* a, uint* al) nothrow @nogc @safe
{
prepAccept(e, fd, *a, *al, AcceptFlags.NONE);
e.user_data = 2LU;
}
, immutable(int), core.sys.posix.netinet.in_.sockaddr_in*, uint*)(ref immutable(int) __param_0, core.sys.posix.netinet.in_.sockaddr_in* __param_1, uint* __param_2) 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().
srv, &(local variable) core.sys.posix.netinet.in_.sockaddr_in peerpeer, &(local variable) uint plenplen);
if ((local variable) during.Uring ioio.int during.Uring.submit(uint want) nothrow @nogc @safeSubmits qued SubmissionEntry to be processed by kernel.
submit(2) < 0)
{
stderr.std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @systemwritefln("submit(connect+accept) failed");
return 1;
}
// Drain both completions. ACCEPT's res is the new server-side fd.
int (local variable) int acceptedFdacceptedFd = -1;
foreach ((local variable) int __; 0 .. 2)
{
(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(ulong) tagtag = (local variable) const(during.io_uring.CompletionEntry) cc.(field) ulong during.io_uring.CompletionEntry.user_datasqe->data submission passed back
user_data;
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;
(local variable) during.Uring ioio.void during.Uring.popFront() pure nothrow @nogc @safeMove to next CompletionEntry
popFront();
if ((local variable) const(ulong) tagtag == (constant) ulong io_uring_tcp_echo.CONNECT_TAG = 1LUCONNECT_TAG)
{
if ((local variable) const(int) resres < 0)
{
stderr.std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @systemwritefln("CONNECT failed: errno %d", -(local variable) const(int) resres);
return 1;
}
}
else if ((local variable) const(ulong) tagtag == (constant) ulong io_uring_tcp_echo.ACCEPT_TAG = 2LUACCEPT_TAG)
{
if ((local variable) const(int) resres < 0)
{
stderr.std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @systemwritefln("ACCEPT failed: errno %d", -(local variable) const(int) resres);
return 1;
}
(local variable) int acceptedFdacceptedFd = (local variable) const(int) resres; // accept(2) returns the new connected fd
}
}
if ((local variable) int acceptedFdacceptedFd < 0)
{
stderr.std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @systemwritefln("did not obtain an accepted fd");
return 1;
}
scope (exit) int core.sys.posix.unistd.close(int) nothrow @nogc @trustedclose((local variable) int acceptedFdacceptedFd);
// --- 4. SEND on the client, RECV on the accepted server fd -------------
(local variable) during.Uring ioio.putWith!((ref SubmissionEntry e, int fd) {
e.prepSend(fd, PAYLOAD);
e.user_data = SEND_TAG;
})(during.Uring during.Uring.putWith!(function (ref during.io_uring.SubmissionEntry e, int fd) nothrow @nogc @safe
{
prepSend(e, fd, cast(const(ubyte)[])PAYLOAD, MsgFlags.NONE);
e.user_data = 3LU;
}
, immutable(int))(ref immutable(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().
cli);
ubyte[64] (local variable) ubyte[64] rxbufrxbuf;
(local variable) during.Uring ioio.putWith!((ref SubmissionEntry e, int fd, ubyte[] b) {
e.prepRecv(fd, b);
e.user_data = RECV_TAG;
})(during.Uring during.Uring.putWith!(function (ref during.io_uring.SubmissionEntry e, int fd, ubyte[] b) nothrow @nogc @safe
{
prepRecv(e, fd, b, MsgFlags.NONE);
e.user_data = 4LU;
}
, int, ubyte[])(ref 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().
acceptedFd, (local variable) ubyte[64] rxbufrxbuf[]);
if ((local variable) during.Uring ioio.int during.Uring.submit(uint want) nothrow @nogc @safeSubmits qued SubmissionEntry to be processed by kernel.
submit(2) < 0)
{
stderr.std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @systemwritefln("submit(send+recv) failed");
return 1;
}
// Drain SEND and RECV completions; remember how many bytes RECV produced.
int (local variable) int receivedreceived = -1;
foreach ((local variable) int __; 0 .. 2)
{
(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(ulong) tagtag = (local variable) const(during.io_uring.CompletionEntry) cc.(field) ulong during.io_uring.CompletionEntry.user_datasqe->data submission passed back
user_data;
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;
(local variable) during.Uring ioio.void during.Uring.popFront() pure nothrow @nogc @safeMove to next CompletionEntry
popFront();
if ((local variable) const(ulong) tagtag == (constant) ulong io_uring_tcp_echo.SEND_TAG = 3LUSEND_TAG)
{
if ((local variable) const(int) resres < 0)
{
stderr.std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @systemwritefln("SEND failed: errno %d", -(local variable) const(int) resres);
return 1;
}
}
else if ((local variable) const(ulong) tagtag == (constant) ulong io_uring_tcp_echo.RECV_TAG = 4LURECV_TAG)
{
if ((local variable) const(int) resres < 0)
{
stderr.std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @systemwritefln("RECV failed: errno %d", -(local variable) const(int) resres);
return 1;
}
(local variable) int receivedreceived = (local variable) const(int) resres;
}
}
if ((local variable) int receivedreceived != cast(int) (immutable global) immutable(ubyte[]) io_uring_tcp_echo.PAYLOADPAYLOAD.(field) ulong immutable(ubyte[]).lengthlength || (local variable) ubyte[64] rxbufrxbuf[0 .. (local variable) int receivedreceived] != (immutable global) immutable(ubyte[]) io_uring_tcp_echo.PAYLOADPAYLOAD)
{
stderr.std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @systemwritefln("payload mismatch: sent %d bytes, received %d bytes",
(immutable global) immutable(ubyte[]) io_uring_tcp_echo.PAYLOADPAYLOAD.(field) ulong immutable(ubyte[]).lengthlength, (local variable) int receivedreceived);
return 1;
}
void std.stdio.writefln!(char, ushort, int)(in char[] fmt, ushort __param_1, int __param_2) @safeEquivalent to writef(fmt, args, '\n').
writefln("ok: TCP loopback echo on 127.0.0.1:%d — ACCEPT+CONNECT+SEND+RECV round-tripped %d bytes through one ring",
ushort core.sys.posix.arpa.inet.ntohs(ushort) pure nothrow @nogc @trustedntohs((local variable) core.sys.posix.netinet.in_.sockaddr_in boundbound.(field) ushort core.sys.posix.netinet.in_.sockaddr_in.sin_portsin_port), (local variable) int receivedreceived);
return 0;
}