sendmsg-recvmsg.dhover×272all
#!/usr/bin/env dub
/+ dub.sdl:
    name "io_uring_sendmsg_recvmsg"
    dependency "during" version="~>0.5.0"
    platforms "linux"
    targetPath "build"
+/
/**
 * `io_uring` — network message ops `IORING_OP_SENDMSG` / `IORING_OP_RECVMSG`
 * (Linux 5.3), demonstrated with `SCM_RIGHTS` file-descriptor passing.
 *
 * 5.3 taught `io_uring` to drive the full `sendmsg(2)`/`recvmsg(2)` scatter/gather
 * interface, including the ancillary-data (control-message) channel. This example
 * uses that channel for its most famous trick: passing an open file descriptor
 * across a `AF_UNIX` socket via an `SCM_RIGHTS` control message.
 *
 * The flow:
 *   1. Open a temp file and `socketpair(AF_UNIX)`.
 *   2. Queue a `RECVMSG` on one end and a `SENDMSG` on the other; the send carries
 *      a `cmsghdr{ SOL_SOCKET, SCM_RIGHTS }` whose payload is the temp file's fd.
 *   3. Submit both with one `io_uring_enter`, reap both CQEs.
 *   4. The kernel installs a *new* fd in this process pointing at the same open
 *      file description. We `fstat(2)` both and confirm `st_ino`/`st_dev` match —
 *      proof the descriptor really crossed the socket through the ring.
 *
 * Companion to the io_uring chronology:
 * see docs/research/async-io/io-uring/timeline.md § "5.3 — Network message ops".
 *
 * Run with: `dub run --single sendmsg-recvmsg.d`
 *
 * Portability: prints `SKIP:` and exits 0 if `io_uring` is unavailable or if the
 * kernel rejects SEND/RECVMSG (`-EINVAL`/`-EOPNOTSUPP`); 6.18 supports both.
 */
module 
(module) io_uring_sendmsg_recvmsg

io_uring — network message ops IORING_OP_SENDMSG / IORING_OP_RECVMSG (Linux 5.3), demonstrated with SCM_RIGHTS file-descriptor passing.

5.3 taught io_uring to drive the full sendmsg(2)/recvmsg(2) scatter/gather interface, including the ancillary-data (control-message) channel. This example uses that channel for its most famous trick: passing an open file descriptor across a AF_UNIX socket via an SCM_RIGHTS control message.

The flow:

  1. Open a temp file and socketpair(AF_UNIX).

  2. Queue a RECVMSG on one end and a SENDMSG on the other; the send carries a cmsghdr{ SOL_SOCKET, SCM_RIGHTS } whose payload is the temp file's fd.

  3. Submit both with one io_uring_enter, reap both CQEs.

  4. The kernel installs a new fd in this process pointing at the same open file description. We fstat(2) both and confirm st_ino/st_dev match — proof the descriptor really crossed the socket through the ring.

Companion to the io_uring chronology: see docs/research/async-io/io-uring/timeline.md § "5.3 — Network message ops".

Run with: dub run --single sendmsg-recvmsg.d

Portability

prints SKIP: and exits 0 if io_uring is unavailable or if the kernel rejects SEND/RECVMSG (-EINVAL/-EOPNOTSUPP); 6.18 supports both.

io_uring_sendmsg_recvmsg
;
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.stdc
stdc
.
(module) core.stdc.errno

D header file for C99.

pubs.opengroup.org/onlinepubs/009695399/basedefs/errno.h.html, errno.h

Source

core/stdc/errno.d

@copyrightCopyright Sean Kelly 2005 - 2009.@licenseDistributed under the Boost Software License 1.0. (See accompanying file LICENSE)@authorsSean Kelly, Alex Rønne Petersen@standardsISO/IEC 9899:1999 (E)
errno
:
(alias constant) io_uring_sendmsg_recvmsg.EINVAL = int core.stdc.errno.EINVAL = 22
EINVAL
,
(alias constant) io_uring_sendmsg_recvmsg.EOPNOTSUPP = int core.stdc.errno.EOPNOTSUPP = 95
EOPNOTSUPP
,
(alias constant) io_uring_sendmsg_recvmsg.ENOSYS = int core.stdc.errno.ENOSYS = 38
ENOSYS
;
import
(package) core
core
.
(package) core.sys
sys
.
(package) core.sys.posix
posix
.
(module) core.sys.posix.fcntl

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
fcntl
: open,
(alias constant) io_uring_sendmsg_recvmsg.O_RDWR = int core.sys.posix.fcntl.O_RDWR = 2
O_RDWR
,
(alias constant) io_uring_sendmsg_recvmsg.O_CREAT = int core.sys.posix.fcntl.O_CREAT = 64
O_CREAT
,
(alias constant) io_uring_sendmsg_recvmsg.O_TRUNC = int core.sys.posix.fcntl.O_TRUNC = 512
O_TRUNC
;
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
:
(alias) io_uring_sendmsg_recvmsg.socketpair = int core.sys.posix.sys.socket.socketpair(int, int, int, ref int[2]) nothrow @nogc @safe
socketpair
,
(alias enum value) io_uring_sendmsg_recvmsg.AF_UNIX = core.sys.posix.sys.socket.AF_UNIX = 1
AF_UNIX
,
(alias enum value) io_uring_sendmsg_recvmsg.SOCK_STREAM = core.sys.posix.sys.socket.SOCK_STREAM = 1
SOCK_STREAM
,
(alias enum value) io_uring_sendmsg_recvmsg.SOL_SOCKET = core.sys.posix.sys.socket.SOL_SOCKET = 1
SOL_SOCKET
,
(alias enum value) io_uring_sendmsg_recvmsg.SCM_RIGHTS = core.sys.posix.sys.socket.SCM_RIGHTS = 1u
SCM_RIGHTS
,
(struct) core.sys.posix.sys.socket.msghdr
msghdr
,
(struct) core.sys.posix.sys.socket.cmsghdr
cmsghdr
,
(alias) io_uring_sendmsg_recvmsg.CMSG_FIRSTHDR = inout(core.sys.posix.sys.socket.cmsghdr)* core.sys.posix.sys.socket.CMSG_FIRSTHDR(inout(core.sys.posix.sys.socket.msghdr)* mhdr) pure nothrow @nogc
CMSG_FIRSTHDR
,
(alias) io_uring_sendmsg_recvmsg.CMSG_DATA = inout(ubyte)* core.sys.posix.sys.socket.CMSG_DATA(return scope inout(core.sys.posix.sys.socket.cmsghdr)* cmsg) pure nothrow @nogc
CMSG_DATA
,
(alias) io_uring_sendmsg_recvmsg.CMSG_SPACE = ulong core.sys.posix.sys.socket.CMSG_SPACE(ulong len) pure nothrow @nogc
CMSG_SPACE
,
(alias template) io_uring_sendmsg_recvmsg.CMSG_LEN = core.sys.posix.sys.socket.CMSG_LEN()(size_t len)
CMSG_LEN
;
import
(package) core
core
.
(package) core.sys
sys
.
(package) core.sys.posix
posix
.
(package) core.sys.posix.sys
sys
.
(module) core.sys.posix.sys.stat

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
stat
:
(struct) core.sys.posix.sys.stat.stat_t
stat_t
, fstat;
import
(package) core
core
.
(package) core.sys
sys
.
(package) core.sys.posix
posix
.
(package) core.sys.posix.sys
sys
.
(module) core.sys.posix.sys.uio

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
uio
:
(struct) core.sys.posix.sys.uio.iovec
iovec
;
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_sendmsg_recvmsg.close = int core.sys.posix.unistd.close(int) nothrow @nogc @trusted
close
,
(alias) io_uring_sendmsg_recvmsg.unlink = int core.sys.posix.unistd.unlink(scope const(char*)) nothrow @nogc
unlink
,
(alias) io_uring_sendmsg_recvmsg.write = long core.sys.posix.unistd.write(int, scope const(void*), ulong) nothrow @nogc
write
;
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_sendmsg_recvmsg.writefln = std.stdio.writefln(alias fmt, A...)(A args) if (isSomeString!(typeof(fmt)))

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

writefln
, stderr;
import
(package) std
std
.
(module) std.string

String 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

@seestd.algorithm and std.range for generic range algorithms , std.ascii for functions that work with ASCII strings , std.uni for functions that work with unicode strings@copyrightCopyright The D Language Foundation 2007-.@licenseBoost License 1.0.@authorsWalter Bright, Andrei Alexandrescu, Jonathan M Davis, and David L. 'SpottedTiger' Davis
string
:
(alias) io_uring_sendmsg_recvmsg.toStringz = immutable(char)* std.string.toStringz(scope const(char)[] s) pure nothrow @trusted
@params A D-style string.@returns

A C-style null-terminated string equivalent to s. s must not contain embedded '\0''s as any C function will treat the first '\0' that it sees as the end of the string. If s.empty is true, then a string containing only '\0' is returned.

Important Note: When passing a char* to a C function, and the C function keeps it around for any reason, make sure that you keep a reference to it in your D code. Otherwise, it may become invalid during a garbage collection cycle and cause a nasty bug when the C code tries to use it.

toStringz
;
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; } // ---- A real on-disk file whose fd we will hand to our peer ------------- enum
(constant) string io_uring_sendmsg_recvmsg.main.path = "/tmp/io_uring_scm_rights_demo.tmp"
path
= "/tmp/io_uring_scm_rights_demo.tmp";
const int
(local variable) const(int) srcFd
srcFd
= open(
int core.sys.posix.fcntl.open64(scope const(char*), int, ...) nothrow @nogc
path
.
immutable(char)* std.string.toStringz(scope const(char)[] s) pure nothrow @trusted

Examples

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(""));
@params A D-style string.@returns

A C-style null-terminated string equivalent to s. s must not contain embedded '\0''s as any C function will treat the first '\0' that it sees as the end of the string. If ``s.empty is true, then a string containing only '\0' is returned.

Important Note: When passing a char* to a C function, and the C function keeps it around for any reason, make sure that you keep a reference to it in your D code. Otherwise, it may become invalid during a garbage collection cycle and cause a nasty bug when the C code tries to use it.

toStringz
,
(constant) int core.sys.posix.fcntl.O_RDWR = 2
O_RDWR
|
(constant) int core.sys.posix.fcntl.O_CREAT = 64
O_CREAT
|
(constant) int core.sys.posix.fcntl.O_TRUNC = 512
O_TRUNC
,
(template instance) io_uring_sendmsg_recvmsg.octal!"600"
octal
!"600");
if (
(local variable) const(int) srcFd
srcFd
< 0)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("open(%s) failed",
(constant) string io_uring_sendmsg_recvmsg.main.path = "/tmp/io_uring_scm_rights_demo.tmp"
path
);
return 1; } scope (exit) {
int core.sys.posix.unistd.close(int) nothrow @nogc @trusted
close
(
(local variable) const(int) srcFd
srcFd
);
int core.sys.posix.unistd.unlink(scope const(char*)) nothrow @nogc
unlink
(
(constant) string io_uring_sendmsg_recvmsg.main.path = "/tmp/io_uring_scm_rights_demo.tmp"
path
.
immutable(char)* std.string.toStringz(scope const(char)[] s) pure nothrow @trusted

Examples

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(""));
@params A D-style string.@returns

A C-style null-terminated string equivalent to s. s must not contain embedded '\0''s as any C function will treat the first '\0' that it sees as the end of the string. If ``s.empty is true, then a string containing only '\0' is returned.

Important Note: When passing a char* to a C function, and the C function keeps it around for any reason, make sure that you keep a reference to it in your D code. Otherwise, it may become invalid during a garbage collection cycle and cause a nasty bug when the C code tries to use it.

toStringz
); }
long core.sys.posix.unistd.write(int, scope const(void*), ulong) nothrow @nogc
write
(
(local variable) const(int) srcFd
srcFd
, "io_uring".
(constant) immutable(char)* "io_uring".ptr = "io_uring"
ptr
, 8); // give the file some content / a real inode
(struct) core.sys.posix.sys.stat.stat_t
stat_t
(local variable) core.sys.posix.sys.stat.stat_t srcStat
srcStat
;
if (fstat(
int core.sys.posix.sys.stat.fstat64(int, core.sys.posix.sys.stat.stat_t*) nothrow @nogc @trusted
srcFd
, &
(local variable) core.sys.posix.sys.stat.stat_t srcStat
srcStat
) != 0)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("fstat(srcFd) failed");
return 1; } // ---- The transport: a connected pair of AF_UNIX stream sockets --------- int[2]
(local variable) int[2] sock
sock
;
if (
int core.sys.posix.sys.socket.socketpair(int, int, int, ref int[2]) nothrow @nogc @safe
socketpair
(
(enum value) core.sys.posix.sys.socket.AF_UNIX = 1
AF_UNIX
,
(enum value) core.sys.posix.sys.socket.SOCK_STREAM = 1
SOCK_STREAM
, 0,
(local variable) int[2] sock
sock
) != 0)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("socketpair() failed");
return 1; } scope (exit) {
int core.sys.posix.unistd.close(int) nothrow @nogc @trusted
close
(
(local variable) int[2] sock
sock
[0]);
int core.sys.posix.unistd.close(int) nothrow @nogc @trusted
close
(
(local variable) int[2] sock
sock
[1]); }
// A 1-byte normal payload rides alongside the ancillary fd. SCM_RIGHTS // messages must carry at least one data byte, otherwise the kernel may drop // the ancillary data. ubyte[1]
(local variable) ubyte[1] sendData
sendData
= [0x2A];
ubyte[1]
(local variable) ubyte[1] recvData
recvData
= [0x00];
(struct) core.sys.posix.sys.uio.iovec
iovec
(local variable) core.sys.posix.sys.uio.iovec sendIov
sendIov
=
(struct) core.sys.posix.sys.uio.iovec
iovec
(
(local variable) ubyte[1] sendData
sendData
.
(constant) ubyte* ubyte[1].ptr = &sendData
ptr
,
(local variable) ubyte[1] sendData
sendData
.
(constant) ulong ubyte[1].length = 1LU
length
);
(struct) core.sys.posix.sys.uio.iovec
iovec
(local variable) core.sys.posix.sys.uio.iovec recvIov
recvIov
=
(struct) core.sys.posix.sys.uio.iovec
iovec
(
(local variable) ubyte[1] recvData
recvData
.
(constant) ubyte* ubyte[1].ptr = &recvData
ptr
,
(local variable) ubyte[1] recvData
recvData
.
(constant) ulong ubyte[1].length = 1LU
length
);
// ---- Control-message buffers (the SCM_RIGHTS channel) ------------------ // CMSG_SPACE(int.sizeof) reserves room for one cmsghdr + an aligned int fd. enum
(alias) object.size_t = ulong
size_t
(constant) ulong io_uring_sendmsg_recvmsg.main.controlLen = 24LU
controlLen
=
ulong core.sys.posix.sys.socket.CMSG_SPACE(ulong len) pure nothrow @nogc
CMSG_SPACE
(int.
(constant) ulong int.sizeof = 4LU
sizeof
);
ubyte[
(constant) ulong io_uring_sendmsg_recvmsg.main.controlLen = 24LU
controlLen
]
(local variable) ubyte[24] sendControl
sendControl
= 0;
ubyte[
(constant) ulong io_uring_sendmsg_recvmsg.main.controlLen = 24LU
controlLen
]
(local variable) ubyte[24] recvControl
recvControl
= 0;
(struct) core.sys.posix.sys.socket.msghdr
msghdr
(local variable) core.sys.posix.sys.socket.msghdr sendMsg
sendMsg
;
(local variable) core.sys.posix.sys.socket.msghdr sendMsg
sendMsg
.
(field) core.sys.posix.sys.uio.iovec* core.sys.posix.sys.socket.msghdr.msg_iov
msg_iov
= &
(local variable) core.sys.posix.sys.uio.iovec sendIov
sendIov
;
(local variable) core.sys.posix.sys.socket.msghdr sendMsg
sendMsg
.
(field) ulong core.sys.posix.sys.socket.msghdr.msg_iovlen
msg_iovlen
= 1;
(local variable) core.sys.posix.sys.socket.msghdr sendMsg
sendMsg
.
(field) void* core.sys.posix.sys.socket.msghdr.msg_control
msg_control
=
(local variable) ubyte[24] sendControl
sendControl
.
(constant) ubyte* ubyte[24].ptr = &sendControl
ptr
;
(local variable) core.sys.posix.sys.socket.msghdr sendMsg
sendMsg
.
(field) ulong core.sys.posix.sys.socket.msghdr.msg_controllen
msg_controllen
=
(constant) ulong io_uring_sendmsg_recvmsg.main.controlLen = 24LU
controlLen
;
// Fill the single control message: level SOL_SOCKET, type SCM_RIGHTS, // payload = the fd we want the peer to receive.
(struct) core.sys.posix.sys.socket.cmsghdr
cmsghdr
*
(local variable) core.sys.posix.sys.socket.cmsghdr* cm
cm
=
inout(core.sys.posix.sys.socket.cmsghdr)* core.sys.posix.sys.socket.CMSG_FIRSTHDR(inout(core.sys.posix.sys.socket.msghdr)* mhdr) pure nothrow @nogc
CMSG_FIRSTHDR
(&
(local variable) core.sys.posix.sys.socket.msghdr sendMsg
sendMsg
);
(local variable) core.sys.posix.sys.socket.cmsghdr* cm
cm
.
(field) int core.sys.posix.sys.socket.cmsghdr.cmsg_level
cmsg_level
=
(enum value) core.sys.posix.sys.socket.SOL_SOCKET = 1
SOL_SOCKET
;
(local variable) core.sys.posix.sys.socket.cmsghdr* cm
cm
.
(field) int core.sys.posix.sys.socket.cmsghdr.cmsg_type
cmsg_type
=
(enum value) core.sys.posix.sys.socket.SCM_RIGHTS = 1u
SCM_RIGHTS
;
(local variable) core.sys.posix.sys.socket.cmsghdr* cm
cm
.
(field) ulong core.sys.posix.sys.socket.cmsghdr.cmsg_len
cmsg_len
=
ulong core.sys.posix.sys.socket.CMSG_LEN!()(ulong len) pure nothrow @nogc @safe
CMSG_LEN
(int.
(constant) ulong int.sizeof = 4LU
sizeof
);
*(cast(int*)
inout(ubyte)* core.sys.posix.sys.socket.CMSG_DATA(return scope inout(core.sys.posix.sys.socket.cmsghdr)* cmsg) pure nothrow @nogc
CMSG_DATA
(
(local variable) core.sys.posix.sys.socket.cmsghdr* cm
cm
)) =
(local variable) const(int) srcFd
srcFd
;
(struct) core.sys.posix.sys.socket.msghdr
msghdr
(local variable) core.sys.posix.sys.socket.msghdr recvMsg
recvMsg
;
(local variable) core.sys.posix.sys.socket.msghdr recvMsg
recvMsg
.
(field) core.sys.posix.sys.uio.iovec* core.sys.posix.sys.socket.msghdr.msg_iov
msg_iov
= &
(local variable) core.sys.posix.sys.uio.iovec recvIov
recvIov
;
(local variable) core.sys.posix.sys.socket.msghdr recvMsg
recvMsg
.
(field) ulong core.sys.posix.sys.socket.msghdr.msg_iovlen
msg_iovlen
= 1;
(local variable) core.sys.posix.sys.socket.msghdr recvMsg
recvMsg
.
(field) void* core.sys.posix.sys.socket.msghdr.msg_control
msg_control
=
(local variable) ubyte[24] recvControl
recvControl
.
(constant) ubyte* ubyte[24].ptr = &recvControl
ptr
;
(local variable) core.sys.posix.sys.socket.msghdr recvMsg
recvMsg
.
(field) ulong core.sys.posix.sys.socket.msghdr.msg_controllen
msg_controllen
=
(constant) ulong io_uring_sendmsg_recvmsg.main.controlLen = 24LU
controlLen
;
// ---- Queue RECVMSG (user_data 0) then SENDMSG (user_data 1) ------------ // Posting the receive first guarantees there is a reader waiting; both // complete asynchronously off the same submission.
(local variable) during.Uring io
io
.putWith!((ref SubmissionEntry e, int fd, ref msghdr m) {
e.prepRecvMsg(fd, m); e.user_data = 0; })(
during.Uring during.Uring.putWith!(function (ref during.io_uring.SubmissionEntry e, int fd, ref core.sys.posix.sys.socket.msghdr m) nothrow @nogc @safe { prepRecvMsg(e, fd, m, MsgFlags.NONE); e.user_data = 0LU; } , int, core.sys.posix.sys.socket.msghdr)(ref int __param_0, ref core.sys.posix.sys.socket.msghdr __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.
sock
[0],
(local variable) core.sys.posix.sys.socket.msghdr recvMsg
recvMsg
);
(local variable) during.Uring io
io
.putWith!((ref SubmissionEntry e, int fd, ref msghdr m) {
e.prepSendMsg(fd, m); e.user_data = 1; })(
during.Uring during.Uring.putWith!(function (ref during.io_uring.SubmissionEntry e, int fd, ref core.sys.posix.sys.socket.msghdr m) nothrow @nogc @safe { prepSendMsg(e, fd, m, MsgFlags.NONE); e.user_data = 1LU; } , int, core.sys.posix.sys.socket.msghdr)(ref int __param_0, ref core.sys.posix.sys.socket.msghdr __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.
sock
[1],
(local variable) core.sys.posix.sys.socket.msghdr sendMsg
sendMsg
);
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; capture the received fd from the recv CQE -- int
(local variable) int recvRes
recvRes
= int.
(constant) int int.min = -2147483648
min
;
int
(local variable) int sendRes
sendRes
= int.
(constant) int int.min = -2147483648
min
;
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(ulong) ud
ud
=
(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
.
(field) ulong during.io_uring.CompletionEntry.user_data

sqe->data submission passed back

user_data
;
const
(local variable) const(int) res
res
=
(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
.
(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) ud
ud
== 0)
(local variable) int recvRes
recvRes
=
(local variable) const(int) res
res
;
else
(local variable) int sendRes
sendRes
=
(local variable) const(int) res
res
;
} // Either op returning -EINVAL/-EOPNOTSUPP/-ENOSYS means this kernel lacks // SEND/RECVMSG support — that is an expected SKIP, not a failure. foreach (
(parameter) int res
res
; [
(local variable) int recvRes
recvRes
,
(local variable) int sendRes
sendRes
])
{ if (
(local variable) int res
res
== -
(constant) int core.stdc.errno.EINVAL = 22
EINVAL
||
(local variable) int res
res
== -
(constant) int core.stdc.errno.EOPNOTSUPP = 95
EOPNOTSUPP
||
(local variable) int res
res
== -
(constant) int core.stdc.errno.ENOSYS = 38
ENOSYS
)
{
void std.stdio.writefln!(char, int)(in char[] fmt, int __param_1) @safe

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

writefln
("SKIP: kernel rejected SEND/RECVMSG (errno %d) — unsupported here", -
(local variable) int res
res
);
return 0; } } if (
(local variable) int sendRes
sendRes
< 0)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("SENDMSG completed with error: errno %d", -
(local variable) int sendRes
sendRes
);
return 1; } if (
(local variable) int recvRes
recvRes
< 0)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("RECVMSG completed with error: errno %d", -
(local variable) int recvRes
recvRes
);
return 1; } // ---- Extract the passed fd from the received control message -----------
(struct) core.sys.posix.sys.socket.cmsghdr
cmsghdr
*
(local variable) core.sys.posix.sys.socket.cmsghdr* rcm
rcm
=
inout(core.sys.posix.sys.socket.cmsghdr)* core.sys.posix.sys.socket.CMSG_FIRSTHDR(inout(core.sys.posix.sys.socket.msghdr)* mhdr) pure nothrow @nogc
CMSG_FIRSTHDR
(&
(local variable) core.sys.posix.sys.socket.msghdr recvMsg
recvMsg
);
if (
(local variable) core.sys.posix.sys.socket.cmsghdr* rcm
rcm
is null ||
(local variable) core.sys.posix.sys.socket.cmsghdr* rcm
rcm
.
(field) int core.sys.posix.sys.socket.cmsghdr.cmsg_level
cmsg_level
!=
(enum value) core.sys.posix.sys.socket.SOL_SOCKET = 1
SOL_SOCKET
||
(local variable) core.sys.posix.sys.socket.cmsghdr* rcm
rcm
.
(field) int core.sys.posix.sys.socket.cmsghdr.cmsg_type
cmsg_type
!=
(enum value) core.sys.posix.sys.socket.SCM_RIGHTS = 1u
SCM_RIGHTS
)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("no SCM_RIGHTS control message received");
return 1; } const int
(local variable) const(int) passedFd
passedFd
= *(cast(int*)
inout(ubyte)* core.sys.posix.sys.socket.CMSG_DATA(return scope inout(core.sys.posix.sys.socket.cmsghdr)* cmsg) pure nothrow @nogc
CMSG_DATA
(
(local variable) core.sys.posix.sys.socket.cmsghdr* rcm
rcm
));
if (
(local variable) const(int) passedFd
passedFd
< 0)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("invalid received fd %d",
(local variable) const(int) passedFd
passedFd
);
return 1; } scope (exit)
int core.sys.posix.unistd.close(int) nothrow @nogc @trusted
close
(
(local variable) const(int) passedFd
passedFd
);
// The clincher: the received fd is a brand-new descriptor number, yet it // refers to the same open file. Matching device + inode proves it.
(struct) core.sys.posix.sys.stat.stat_t
stat_t
(local variable) core.sys.posix.sys.stat.stat_t passedStat
passedStat
;
if (fstat(
int core.sys.posix.sys.stat.fstat64(int, core.sys.posix.sys.stat.stat_t*) nothrow @nogc @trusted
passedFd
, &
(local variable) core.sys.posix.sys.stat.stat_t passedStat
passedStat
) != 0)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("fstat(passedFd) failed");
return 1; } if (
(local variable) core.sys.posix.sys.stat.stat_t passedStat
passedStat
.
(field) ulong core.sys.posix.sys.stat.stat_t.st_dev
st_dev
!=
(local variable) core.sys.posix.sys.stat.stat_t srcStat
srcStat
.
(field) ulong core.sys.posix.sys.stat.stat_t.st_dev
st_dev
||
(local variable) core.sys.posix.sys.stat.stat_t passedStat
passedStat
.
(field) ulong core.sys.posix.sys.stat.stat_t.st_ino
st_ino
!=
(local variable) core.sys.posix.sys.stat.stat_t srcStat
srcStat
.
(field) ulong core.sys.posix.sys.stat.stat_t.st_ino
st_ino
)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("received fd refers to a different file (dev/ino mismatch)");
return 1; } if (
(local variable) const(int) passedFd
passedFd
==
(local variable) const(int) srcFd
srcFd
)
{ stderr.
std.stdio.File std.stdio.makeGlobal!"core.stdc.stdio.stderr"() nothrow @nogc @property ref @system
writefln
("received fd should be a distinct descriptor number, got the same one");
return 1; }
void std.stdio.writefln!(char, const(int), const(int), ulong, ulong)(in char[] fmt, const(int) __param_1, const(int) __param_2, ulong __param_3, ulong __param_4) @safe

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

writefln
(
"ok: SCM_RIGHTS fd-passing via io_uring SENDMSG/RECVMSG — sent fd %d, received fd %d, same file (dev=%d ino=%d)",
(local variable) const(int) srcFd
srcFd
,
(local variable) const(int) passedFd
passedFd
,
(local variable) core.sys.posix.sys.stat.stat_t passedStat
passedStat
.
(field) ulong core.sys.posix.sys.stat.stat_t.st_dev
st_dev
,
(local variable) core.sys.posix.sys.stat.stat_t passedStat
passedStat
.
(field) ulong core.sys.posix.sys.stat.stat_t.st_ino
st_ino
);
return 0; } // `0o600`-style octal literal helper (D dropped the `0o`/`0NNN` syntax). private template
(template) io_uring_sendmsg_recvmsg.octal(string s)
octal
(string s)
{ enum uint
(constant) uint io_uring_sendmsg_recvmsg.octal!"600" = 384u
octal
= {
uint
(local variable) uint v
v
= 0;
foreach (
(parameter) immutable(char) c
c
;
(constant) string io_uring_sendmsg_recvmsg.s = "600"
s
)
(local variable) uint v
v
=
(local variable) uint v
v
* 8 + (
(local variable) immutable(char) c
c
- '0');
return
(local variable) uint v
v
;
}(); }