tcp-echo.dhover×214all
#!/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_echo

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.

io_uring_tcp_echo
;
import
(module) during

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

during
;
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_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) core
core
.
(package) core.sys
sys
.
(package) core.sys.posix
posix
.
(package) core.sys.posix.arpa
arpa
.
(module) core.sys.posix.arpa.inet

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
inet
:
(alias) io_uring_tcp_echo.htonl = uint core.sys.posix.arpa.inet.htonl(uint) pure nothrow @nogc @trusted
htonl
,
(alias) io_uring_tcp_echo.ntohs = ushort core.sys.posix.arpa.inet.ntohs(ushort) pure nothrow @nogc @trusted
ntohs
;
import
(package) core
core
.
(package) core.sys
sys
.
(package) core.sys.posix
posix
.
(package) core.sys.posix.netinet
netinet
.
(module) core.sys.posix.netinet.in_

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
in_
;
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
;
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_tcp_echo.close = int core.sys.posix.unistd.close(int) nothrow @nogc @trusted
close
;
// user_data cookies so each completion can be correlated to its op. enum ulong
(constant) ulong io_uring_tcp_echo.CONNECT_TAG = 1LU
CONNECT_TAG
= 1;
enum ulong
(constant) ulong io_uring_tcp_echo.ACCEPT_TAG = 2LU
ACCEPT_TAG
= 2;
enum ulong
(constant) ulong io_uring_tcp_echo.SEND_TAG = 3LU
SEND_TAG
= 3;
enum ulong
(constant) ulong io_uring_tcp_echo.RECV_TAG = 4LU
RECV_TAG
= 4;
// Known payload echoed across loopback. static immutable ubyte[]
(immutable global) immutable(ubyte[]) io_uring_tcp_echo.PAYLOAD
PAYLOAD
= cast(immutable ubyte[]) "io_uring echo \xF0\x9F\x9A\x80";
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; } // 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 probe
probe
=
(local variable) during.Uring io
io
.
during.Probe during.Uring.probe() nothrow @nogc @safe

Probes supported operations

probe
();
if (cast(bool)
(local variable) during.Probe probe
probe
)
{ foreach (
(parameter) during.io_uring.Operation op
op
; [
(enum) during.io_uring.Operation

Describes the operation to be performed

@seeio_uring_enter(2)
Operation
.
(enum value) during.io_uring.Operation.ACCEPT = cast(ubyte)13u

IORING_OP_ACCEPT

ACCEPT
,
(enum) during.io_uring.Operation

Describes the operation to be performed

@seeio_uring_enter(2)
Operation
.
(enum value) during.io_uring.Operation.CONNECT = cast(ubyte)16u

IORING_OP_CONNECT

CONNECT
,
(enum) during.io_uring.Operation

Describes the operation to be performed

@seeio_uring_enter(2)
Operation
.
(enum value) during.io_uring.Operation.SEND = cast(ubyte)26u

IORING_OP_SEND

SEND
,
(enum) during.io_uring.Operation

Describes the operation to be performed

@seeio_uring_enter(2)
Operation
.
(enum value) during.io_uring.Operation.RECV = cast(ubyte)27u

IORING_OP_RECV

RECV
])
{ if (!
(local variable) during.Probe probe
probe
.
bool during.Probe.isSupported(during.io_uring.Operation op) const pure nothrow @nogc @safe

Is operation supported?

isSupported
(
(local variable) during.io_uring.Operation op
op
))
{
void std.stdio.writefln!(char, int)(in char[] fmt, int __param_1) @safe

Equivalent 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 op
op
);
return 0; } } } // --- 1. Listening socket on 127.0.0.1:0 (kernel picks the port) -------- immutable
(local variable) immutable(int) srv
srv
=
int core.sys.posix.sys.socket.socket(int, int, int) nothrow @nogc @safe
socket
(
(enum value) core.sys.posix.sys.socket.AF_INET = 2
AF_INET
,
(enum value) core.sys.posix.sys.socket.SOCK_STREAM = 1
SOCK_STREAM
, 0);
if (
(local variable) immutable(int) srv
srv
< 0)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("socket(srv) failed");
return 1; } scope (exit)
int core.sys.posix.unistd.close(int) nothrow @nogc @trusted
close
(
(local variable) immutable(int) srv
srv
);
int
(local variable) int one
one
= 1;
int core.sys.posix.sys.socket.setsockopt(int, int, int, scope const(void*), uint) nothrow @nogc
setsockopt
(
(local variable) immutable(int) srv
srv
,
(enum value) core.sys.posix.sys.socket.SOL_SOCKET = 1
SOL_SOCKET
,
(enum value) core.sys.posix.sys.socket.SO_REUSEADDR = 2
SO_REUSEADDR
, &
(local variable) int one
one
,
(local variable) int one
one
.
(constant) ulong int.sizeof = 4LU
sizeof
);
(struct) core.sys.posix.netinet.in_.sockaddr_in
sockaddr_in
(local variable) core.sys.posix.netinet.in_.sockaddr_in saddr
saddr
;
(local variable) core.sys.posix.netinet.in_.sockaddr_in saddr
saddr
.
(field) ushort core.sys.posix.netinet.in_.sockaddr_in.sin_family
sin_family
=
(enum value) core.sys.posix.sys.socket.AF_INET = 2
AF_INET
;
(local variable) core.sys.posix.netinet.in_.sockaddr_in saddr
saddr
.
(field) ushort core.sys.posix.netinet.in_.sockaddr_in.sin_port
sin_port
= 0; // kernel-assigned
(local variable) core.sys.posix.netinet.in_.sockaddr_in saddr
saddr
.
(field) core.sys.posix.arpa.inet.in_addr core.sys.posix.netinet.in_.sockaddr_in.sin_addr
sin_addr
.
(field) uint core.sys.posix.arpa.inet.in_addr.s_addr
s_addr
=
uint core.sys.posix.arpa.inet.htonl(uint) pure nothrow @nogc @trusted
htonl
(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 @nogc
bind
(
(local variable) immutable(int) srv
srv
, cast(
(struct) core.sys.posix.sys.socket.sockaddr
sockaddr
*)&
(local variable) core.sys.posix.netinet.in_.sockaddr_in saddr
saddr
,
(struct) core.sys.posix.netinet.in_.sockaddr_in
sockaddr_in
.
(constant) ulong core.sys.posix.netinet.in_.sockaddr_in.sizeof = 16LU
sizeof
) != 0)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("bind() failed");
return 1; } if (
int core.sys.posix.sys.socket.listen(int, int) nothrow @nogc @safe
listen
(
(local variable) immutable(int) srv
srv
, 4) != 0)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("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_in
sockaddr_in
(local variable) core.sys.posix.netinet.in_.sockaddr_in bound
bound
;
(alias) core.sys.posix.sys.socket.socklen_t = uint
socklen_t
(local variable) uint blen
blen
=
(local variable) core.sys.posix.netinet.in_.sockaddr_in bound
bound
.
(constant) ulong core.sys.posix.netinet.in_.sockaddr_in.sizeof = 16LU
sizeof
;
if (
int core.sys.posix.sys.socket.getsockname(int, scope core.sys.posix.sys.socket.sockaddr*, scope uint*) nothrow @nogc
getsockname
(
(local variable) immutable(int) srv
srv
, cast(
(struct) core.sys.posix.sys.socket.sockaddr
sockaddr
*)&
(local variable) core.sys.posix.netinet.in_.sockaddr_in bound
bound
, &
(local variable) uint blen
blen
) != 0)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("getsockname() failed");
return 1; } // --- 2. Client socket -------------------------------------------------- immutable
(local variable) immutable(int) cli
cli
=
int core.sys.posix.sys.socket.socket(int, int, int) nothrow @nogc @safe
socket
(
(enum value) core.sys.posix.sys.socket.AF_INET = 2
AF_INET
,
(enum value) core.sys.posix.sys.socket.SOCK_STREAM = 1
SOCK_STREAM
, 0);
if (
(local variable) immutable(int) cli
cli
< 0)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("socket(cli) failed");
return 1; } scope (exit)
int core.sys.posix.unistd.close(int) nothrow @nogc @trusted
close
(
(local variable) immutable(int) cli
cli
);
// --- 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_in
sockaddr_in
(local variable) core.sys.posix.netinet.in_.sockaddr_in peer
peer
;
(alias) core.sys.posix.sys.socket.socklen_t = uint
socklen_t
(local variable) uint plen
plen
=
(local variable) core.sys.posix.netinet.in_.sockaddr_in peer
peer
.
(constant) ulong core.sys.posix.netinet.in_.sockaddr_in.sizeof = 16LU
sizeof
;
// 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 io
io
.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 @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.
cli
, &
(local variable) core.sys.posix.netinet.in_.sockaddr_in bound
bound
);
(local variable) during.Uring io
io
.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 @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.
srv
, &
(local variable) core.sys.posix.netinet.in_.sockaddr_in peer
peer
, &
(local variable) uint plen
plen
);
if (
(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) < 0)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("submit(connect+accept) failed");
return 1; } // Drain both completions. ACCEPT's res is the new server-side fd. int
(local variable) int acceptedFd
acceptedFd
= -1;
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) 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(ulong) tag
tag
=
(local variable) const(during.io_uring.CompletionEntry) c
c
.
(field) ulong during.io_uring.CompletionEntry.user_data

sqe->data submission passed back

user_data
;
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
;
(local variable) during.Uring io
io
.
void during.Uring.popFront() pure nothrow @nogc @safe

Move to next CompletionEntry

popFront
();
if (
(local variable) const(ulong) tag
tag
==
(constant) ulong io_uring_tcp_echo.CONNECT_TAG = 1LU
CONNECT_TAG
)
{ if (
(local variable) const(int) res
res
< 0)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("CONNECT failed: errno %d", -
(local variable) const(int) res
res
);
return 1; } } else if (
(local variable) const(ulong) tag
tag
==
(constant) ulong io_uring_tcp_echo.ACCEPT_TAG = 2LU
ACCEPT_TAG
)
{ if (
(local variable) const(int) res
res
< 0)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("ACCEPT failed: errno %d", -
(local variable) const(int) res
res
);
return 1; }
(local variable) int acceptedFd
acceptedFd
=
(local variable) const(int) res
res
; // accept(2) returns the new connected fd
} } if (
(local variable) int acceptedFd
acceptedFd
< 0)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("did not obtain an accepted fd");
return 1; } scope (exit)
int core.sys.posix.unistd.close(int) nothrow @nogc @trusted
close
(
(local variable) int acceptedFd
acceptedFd
);
// --- 4. SEND on the client, RECV on the accepted server fd -------------
(local variable) during.Uring io
io
.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 @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.
cli
);
ubyte[64]
(local variable) ubyte[64] rxbuf
rxbuf
;
(local variable) during.Uring io
io
.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 @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.
acceptedFd
,
(local variable) ubyte[64] rxbuf
rxbuf
[]);
if (
(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) < 0)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("submit(send+recv) failed");
return 1; } // Drain SEND and RECV completions; remember how many bytes RECV produced. int
(local variable) int received
received
= -1;
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) 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(ulong) tag
tag
=
(local variable) const(during.io_uring.CompletionEntry) c
c
.
(field) ulong during.io_uring.CompletionEntry.user_data

sqe->data submission passed back

user_data
;
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
;
(local variable) during.Uring io
io
.
void during.Uring.popFront() pure nothrow @nogc @safe

Move to next CompletionEntry

popFront
();
if (
(local variable) const(ulong) tag
tag
==
(constant) ulong io_uring_tcp_echo.SEND_TAG = 3LU
SEND_TAG
)
{ if (
(local variable) const(int) res
res
< 0)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("SEND failed: errno %d", -
(local variable) const(int) res
res
);
return 1; } } else if (
(local variable) const(ulong) tag
tag
==
(constant) ulong io_uring_tcp_echo.RECV_TAG = 4LU
RECV_TAG
)
{ if (
(local variable) const(int) res
res
< 0)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("RECV failed: errno %d", -
(local variable) const(int) res
res
);
return 1; }
(local variable) int received
received
=
(local variable) const(int) res
res
;
} } if (
(local variable) int received
received
!= cast(int)
(immutable global) immutable(ubyte[]) io_uring_tcp_echo.PAYLOAD
PAYLOAD
.
(field) ulong immutable(ubyte[]).length
length
||
(local variable) ubyte[64] rxbuf
rxbuf
[0 ..
(local variable) int received
received
] !=
(immutable global) immutable(ubyte[]) io_uring_tcp_echo.PAYLOAD
PAYLOAD
)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("payload mismatch: sent %d bytes, received %d bytes",
(immutable global) immutable(ubyte[]) io_uring_tcp_echo.PAYLOAD
PAYLOAD
.
(field) ulong immutable(ubyte[]).length
length
,
(local variable) int received
received
);
return 1; }
void std.stdio.writefln!(char, ushort, int)(in char[] fmt, ushort __param_1, int __param_2) @safe

Equivalent 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 @trusted
ntohs
(
(local variable) core.sys.posix.netinet.in_.sockaddr_in bound
bound
.
(field) ushort core.sys.posix.netinet.in_.sockaddr_in.sin_port
sin_port
),
(local variable) int received
received
);
return 0; }