#!/usr/bin/env dub
/+ dub.sdl:
name "eh_callback_tcp_echo"
dependency "sparkles:event-horizon" path="../../../../.."
platforms "linux"
buildType "checked" {
buildOptions "optimize" "inline" "debugInfo"
}
+/
@system:
import (package) corecore.(module) core.lifetimelifetime : (alias template) eh_callback_tcp_echo.move = core.lifetime.move(T)(ref T source, ref T target)Moves source into target, via a destructive copy when necessary.
If T is a struct with a destructor or postblit defined, source is reset
to its .init value after it is moved into target, otherwise it is
left unchanged.
Preconditions
If source has internal pointers that point to itself and doesn't define
opPostMove, it cannot be moved, and will trigger an assertion failure.
move;
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.(package) core.sys.posix.netinetnetinet.(module) core.sys.posix.netinet.in_D header file for POSIX.
in_ : (struct) core.sys.posix.netinet.in_.sockaddr_insockaddr_in, (alias enum value) eh_callback_tcp_echo.INADDR_LOOPBACK = core.sys.posix.netinet.in_.INADDR_LOOPBACK = 2130706433uINADDR_LOOPBACK;
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) eh_callback_tcp_echo.htonl = uint core.sys.posix.arpa.inet.htonl(uint) pure nothrow @nogc @trustedhtonl;
import (package) corecore.(package) core.syssys.(package) core.sys.posixposix.(module) core.sys.posix.unistdD header file for POSIX.
unistd : (alias) eh_callback_tcp_echo.close = int core.sys.posix.unistd.close(int) nothrow @nogc @trustedclose;
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) eh_callback_tcp_echo.writeln = std.stdio.writeln(T...)(T args)Equivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln;
import (package) sparklessparkles.(package) sparkles.event_horizonevent_horizon.(module) sparkles.event_horizon.loopTier A — the callback completion core (SPEC §5): submit(op, callback, ctx)
over one DbI backend. The loop owns the op-slot slab (buffer pinning,
kernel-stable operands, the cancellation state machine) and delivers
completions only inside runOnce — the DEFER_TASKRUN contract made
structural.
The loop is thread-affine and non-copyable: every member must be called from
the owning thread — except Waker.wake() (SPEC §5.6), the one off-thread
door, obtained via waker().
loop : (struct) sparkles.event_horizon.loop.EventLoop!(UringBackend, Mallocator)DefaultLoop;
import (package) sparklessparkles.(package) sparkles.event_horizonevent_horizon.(module) sparkles.event_horizon.opPortable operation vocabulary: typed op descriptors, the user_data token
discipline, the op-slot slab with its lifetime state machine, and the
completion shape (SPEC §4). This module is portable — it imports no backend
and never sees an SQE; backend-specific lowering lives in
sparkles.event_horizon.backend.*.
op;
import (package) sparklessparkles.(package) sparkles.event_horizonevent_horizon.(module) sparkles.event_horizon.bufferOwned-buffer currency for the completion window (SPEC §6).
Buf is a move-only handle over pinned, stable memory — a pool slab
slot, a registered-buffer slot, a provided-ring lease, or vouched-for foreign
memory. The kernel holds the pointer from submission to the terminal
completion, so the bytes must not move or be freed in that window; that is
why SharedBuffer (whose small-buffer optimization relocates the payload on
struct moves) is banned as the tier-A transfer currency.
Memory management follows the
composable-allocator guidelines:
the pool is generic over an Allocator (the §4 recipe — stateSize embed,
Mallocator default, attributes inferred) and draws its backing slab through
makeArray/dispose. The slot-index structure stays bespoke: slot indices
are the buf_index/bid currency for READ_FIXED and provided rings, which
a plain FreeList cannot provide. Page-aligned parents
(MmapAllocator/AscendingPageAllocator) suit pools destined for buffer
registration.
buffer : (alias struct) eh_callback_tcp_echo.BufferPool = sparkles.event_horizon.buffer.BufferPool(Allocator = Mallocator)Fixed pool of same-size buffers carved out of one contiguous slab drawn from
Allocator (SPEC §6.3) — the guideline-§4 generic-library shape: monostate
allocators cost zero bytes (stateSize idiom), stateful ones are passed to
create and embedded. When the backend supports registered buffers the slab
is registered once and every Buf carries its slot index; lowering then
picks the FIXED opcodes purely from Buf.origin — the user never spells
"fixed". (Registration wiring lands in M8; until then every pool Buf has
BufOrigin.pool.)
BufferPool;
enum (constant) @system string eh_callback_tcp_echo.payload = "hello"payload = "hello";
struct (struct) eh_callback_tcp_echo.EndpointEndpoint
{
(struct) sparkles.event_horizon.loop.EventLoop!(UringBackend, Mallocator)DefaultLoop* (field) @system sparkles.event_horizon.loop.EventLoop!(UringBackend, Mallocator)* eh_callback_tcp_echo.Endpoint.looploop;
(struct) sparkles.event_horizon.buffer.BufferPool!(std.experimental.allocator.mallocator.Mallocator)Fixed pool of same-size buffers carved out of one contiguous slab drawn from
Allocator (SPEC §6.3) — the guideline-§4 generic-library shape: monostate
allocators cost zero bytes (stateSize idiom), stateful ones are passed to
create and embedded. When the backend supports registered buffers the slab
is registered once and every Buf carries its slot index; lowering then
picks the FIXED opcodes purely from Buf.origin — the user never spells
"fixed". (Registration wiring lands in M8; until then every pool Buf has
BufOrigin.pool.)
BufferPool!()* (field) @system sparkles.event_horizon.buffer.BufferPool!(Mallocator)* eh_callback_tcp_echo.Endpoint.poolpool;
int (field) @system int eh_callback_tcp_echo.Endpoint.fdfd = -1;
bool (field) @system bool eh_callback_tcp_echo.Endpoint.serverserver;
(alias) object.size_t = ulongsize_t (field) @system ulong eh_callback_tcp_echo.Endpoint.sentsent, (field) @system ulong eh_callback_tcp_echo.Endpoint.receivedreceived, (field) @system ulong eh_callback_tcp_echo.Endpoint.pendingSendpendingSend;
char[5] (field) @system char[5] eh_callback_tcp_echo.Endpoint.bytesbytes;
}
void void eh_callback_tcp_echo.receive(eh_callback_tcp_echo.Endpoint* endpoint) nothrow @nogc @systemreceive((struct) eh_callback_tcp_echo.EndpointEndpoint* (parameter) eh_callback_tcp_echo.Endpoint* endpointendpoint) nothrow @nogc
{
auto (local variable) expected.Expected!(Buf, IoError, NoGcHook) bufferbuffer = (parameter) eh_callback_tcp_echo.Endpoint* endpointendpoint.(field) @system sparkles.event_horizon.buffer.BufferPool!(Mallocator)* eh_callback_tcp_echo.Endpoint.poolpool.expected.Expected!(Buf, IoError, NoGcHook) sparkles.event_horizon.buffer.BufferPool!(std.experimental.allocator.mallocator.Mallocator).acquire() nothrow @nogc @trustedHands out one buffer; ENOBUFS when the pool is exhausted.
acquire();
assert((local variable) expected.Expected!(Buf, IoError, NoGcHook) bufferbuffer.bool expected.Expected!(sparkles.event_horizon.buffer.Buf, sparkles.base.io.errors.IoError, sparkles.base.text.errors.NoGcHook).hasValue!()() const pure nothrow @nogc @property @safeChecks if Expected has value
hasValue);
assert((parameter) eh_callback_tcp_echo.Endpoint* endpointendpoint.(field) @system sparkles.event_horizon.loop.EventLoop!(UringBackend, Mallocator)* eh_callback_tcp_echo.Endpoint.looploop.expected.Expected!(OpHandle, IoError, NoGcHook) sparkles.event_horizon.loop.EventLoop!(sparkles.event_horizon.backend.uring.UringBackend, std.experimental.allocator.mallocator.Mallocator).submit!(sparkles.event_horizon.op.OpRecv)(sparkles.event_horizon.op.OpRecv op, void function(void* ctx, ref sparkles.event_horizon.op.Completion done) nothrow @nogc cb, void* ctx = null) nothrow @safeSubmits op with a completion callback (SPEC §5.2). Owned buffers move
into the op slot and come back via Completion.buf; on a submission
error the buffer is recycled to its origin (it does not come back —
there will be no completion).
The ctx lifetime rule: ctx must stay valid until the op's
terminal completion is dispatched — cancel alone does not shorten
that (the terminal CQE still arrives and still invokes cb). A caller
whose ctx dies earlier must detach the handle first: after detach
returns, the callback never runs. Handing a stack frame's address to
ctx without one of those guarantees is exactly the use-after-free the
retired kernel-timer deadline shipped (SPEC §8.3).
Backpressure
on a full submission queue the loop performs one implicit
flush retry, then returns EAGAIN; an exhausted slab returns
ENOBUFS.
submit((struct) sparkles.event_horizon.op.OpRecvSocket receive into the buffer's full capacity.
OpRecv((parameter) eh_callback_tcp_echo.Endpoint* endpointendpoint.(field) @system int eh_callback_tcp_echo.Endpoint.fdfd, sparkles.event_horizon.buffer.Buf core.lifetime.move!(sparkles.event_horizon.buffer.Buf)(ref return scope sparkles.event_horizon.buffer.Buf source) nothrow @nogc @safeExamples
Non-copyable structs can still be moved:
struct S
{
int a = 1;
@disable this(this);
~this() pure nothrow @safe @nogc {}
}
S s1;
s1.a = 2;
S s2 = move(s1);
assert(s1.a == 1);
assert(s2.a == 2);
move((local variable) expected.Expected!(Buf, IoError, NoGcHook) bufferbuffer.inout(sparkles.event_horizon.buffer.Buf) expected.Expected!(sparkles.event_horizon.buffer.Buf, sparkles.base.io.errors.IoError, sparkles.base.text.errors.NoGcHook).value!()() inout pure nothrow @nogc @property ref @safeReturns the expected value if there is one.
With default Abort hook, it asserts when there is no value.
It calls hook's onAccessEmptyValue otherwise.
It returns T.init when hook doesn't provide onAccessEmptyValue.
value)),
&void eh_callback_tcp_echo.received(void* context, ref sparkles.event_horizon.op.Completion done) nothrow @nogc @systemreceived, (parameter) eh_callback_tcp_echo.Endpoint* endpointendpoint).bool expected.Expected!(sparkles.event_horizon.op.OpHandle, sparkles.base.io.errors.IoError, sparkles.base.text.errors.NoGcHook).hasValue!()() const pure nothrow @nogc @property @safeChecks if Expected has value
hasValue);
}
void void eh_callback_tcp_echo.send(eh_callback_tcp_echo.Endpoint* endpoint) nothrow @nogc @systemsend((struct) eh_callback_tcp_echo.EndpointEndpoint* (parameter) eh_callback_tcp_echo.Endpoint* endpointendpoint) nothrow @nogc
{
auto (local variable) expected.Expected!(Buf, IoError, NoGcHook) bufferbuffer = (parameter) eh_callback_tcp_echo.Endpoint* endpointendpoint.(field) @system sparkles.event_horizon.buffer.BufferPool!(Mallocator)* eh_callback_tcp_echo.Endpoint.poolpool.expected.Expected!(Buf, IoError, NoGcHook) sparkles.event_horizon.buffer.BufferPool!(std.experimental.allocator.mallocator.Mallocator).acquire() nothrow @nogc @trustedHands out one buffer; ENOBUFS when the pool is exhausted.
acquire();
assert((local variable) expected.Expected!(Buf, IoError, NoGcHook) bufferbuffer.bool expected.Expected!(sparkles.event_horizon.buffer.Buf, sparkles.base.io.errors.IoError, sparkles.base.text.errors.NoGcHook).hasValue!()() const pure nothrow @nogc @property @safeChecks if Expected has value
hasValue);
// Two-byte buffers force the five-byte frame through several operations.
auto (local variable) ulong remainingremaining = (constant) @system string eh_callback_tcp_echo.payload = "hello"payload.(constant) ulong "hello".length = 5LUlength - (parameter) eh_callback_tcp_echo.Endpoint* endpointendpoint.(field) @system ulong eh_callback_tcp_echo.Endpoint.sentsent;
auto (local variable) ulong countcount = (local variable) ulong remainingremaining < 2 ? (local variable) ulong remainingremaining : 2;
const(char)[] (local variable) const(char)[] outgoingoutgoing = (parameter) eh_callback_tcp_echo.Endpoint* endpointendpoint.(field) @system bool eh_callback_tcp_echo.Endpoint.serverserver ? (parameter) eh_callback_tcp_echo.Endpoint* endpointendpoint.(field) @system char[5] eh_callback_tcp_echo.Endpoint.bytesbytes[] : (constant) @system string eh_callback_tcp_echo.payload = "hello"payload;
(local variable) expected.Expected!(Buf, IoError, NoGcHook) bufferbuffer.inout(sparkles.event_horizon.buffer.Buf) expected.Expected!(sparkles.event_horizon.buffer.Buf, sparkles.base.io.errors.IoError, sparkles.base.text.errors.NoGcHook).value!()() inout pure nothrow @nogc @property ref @safeReturns the expected value if there is one.
With default Abort hook, it asserts when there is no value.
It calls hook's onAccessEmptyValue otherwise.
It returns T.init when hook doesn't provide onAccessEmptyValue.
value.inout(ubyte)[] sparkles.event_horizon.buffer.Buf.space() inout pure nothrow @nogc return scope @trustedFull-capacity view for receive paths: the kernel fills it, and the
caller sets length from the completion's byte count.
space()[0 .. (local variable) ulong countcount] = cast(const(ubyte)[]) (local variable) const(char)[] outgoingoutgoing[(parameter) eh_callback_tcp_echo.Endpoint* endpointendpoint.(field) @system ulong eh_callback_tcp_echo.Endpoint.sentsent .. (parameter) eh_callback_tcp_echo.Endpoint* endpointendpoint.(field) @system ulong eh_callback_tcp_echo.Endpoint.sentsent + (local variable) ulong countcount];
(local variable) expected.Expected!(Buf, IoError, NoGcHook) bufferbuffer.inout(sparkles.event_horizon.buffer.Buf) expected.Expected!(sparkles.event_horizon.buffer.Buf, sparkles.base.io.errors.IoError, sparkles.base.text.errors.NoGcHook).value!()() inout pure nothrow @nogc @property ref @safeReturns the expected value if there is one.
With default Abort hook, it asserts when there is no value.
It calls hook's onAccessEmptyValue otherwise.
It returns T.init when hook doesn't provide onAccessEmptyValue.
value.void sparkles.event_horizon.buffer.Buf.length(uint n) pure nothrow @nogc @safeSets the valid-byte count (e.g. before a send); capped by capacity.
length = cast(uint) (local variable) ulong countcount;
(parameter) eh_callback_tcp_echo.Endpoint* endpointendpoint.(field) @system ulong eh_callback_tcp_echo.Endpoint.pendingSendpendingSend = (local variable) ulong countcount;
assert((parameter) eh_callback_tcp_echo.Endpoint* endpointendpoint.(field) @system sparkles.event_horizon.loop.EventLoop!(UringBackend, Mallocator)* eh_callback_tcp_echo.Endpoint.looploop.expected.Expected!(OpHandle, IoError, NoGcHook) sparkles.event_horizon.loop.EventLoop!(sparkles.event_horizon.backend.uring.UringBackend, std.experimental.allocator.mallocator.Mallocator).submit!(sparkles.event_horizon.op.OpSend)(sparkles.event_horizon.op.OpSend op, void function(void* ctx, ref sparkles.event_horizon.op.Completion done) nothrow @nogc cb, void* ctx = null) nothrow @safeSubmits op with a completion callback (SPEC §5.2). Owned buffers move
into the op slot and come back via Completion.buf; on a submission
error the buffer is recycled to its origin (it does not come back —
there will be no completion).
The ctx lifetime rule: ctx must stay valid until the op's
terminal completion is dispatched — cancel alone does not shorten
that (the terminal CQE still arrives and still invokes cb). A caller
whose ctx dies earlier must detach the handle first: after detach
returns, the callback never runs. Handing a stack frame's address to
ctx without one of those guarantees is exactly the use-after-free the
retired kernel-timer deadline shipped (SPEC §8.3).
Backpressure
on a full submission queue the loop performs one implicit
flush retry, then returns EAGAIN; an exhausted slab returns
ENOBUFS.
submit((struct) sparkles.event_horizon.op.OpSendSocket send of the buffer's valid bytes.
OpSend((parameter) eh_callback_tcp_echo.Endpoint* endpointendpoint.(field) @system int eh_callback_tcp_echo.Endpoint.fdfd, sparkles.event_horizon.buffer.Buf core.lifetime.move!(sparkles.event_horizon.buffer.Buf)(ref return scope sparkles.event_horizon.buffer.Buf source) nothrow @nogc @safeExamples
Non-copyable structs can still be moved:
struct S
{
int a = 1;
@disable this(this);
~this() pure nothrow @safe @nogc {}
}
S s1;
s1.a = 2;
S s2 = move(s1);
assert(s1.a == 1);
assert(s2.a == 2);
move((local variable) expected.Expected!(Buf, IoError, NoGcHook) bufferbuffer.inout(sparkles.event_horizon.buffer.Buf) expected.Expected!(sparkles.event_horizon.buffer.Buf, sparkles.base.io.errors.IoError, sparkles.base.text.errors.NoGcHook).value!()() inout pure nothrow @nogc @property ref @safeReturns the expected value if there is one.
With default Abort hook, it asserts when there is no value.
It calls hook's onAccessEmptyValue otherwise.
It returns T.init when hook doesn't provide onAccessEmptyValue.
value)),
&void eh_callback_tcp_echo.sent(void* context, ref sparkles.event_horizon.op.Completion done) nothrow @nogc @systemsent, (parameter) eh_callback_tcp_echo.Endpoint* endpointendpoint).bool expected.Expected!(sparkles.event_horizon.op.OpHandle, sparkles.base.io.errors.IoError, sparkles.base.text.errors.NoGcHook).hasValue!()() const pure nothrow @nogc @property @safeChecks if Expected has value
hasValue);
}
void void eh_callback_tcp_echo.sent(void* context, ref sparkles.event_horizon.op.Completion done) nothrow @nogc @systemsent(void* (parameter) void* contextcontext, ref (struct) sparkles.event_horizon.op.CompletionWhat a tier-A callback receives. Passed by ref: the callback may
move(done.buf) to keep the buffer; otherwise the loop recycles it to its
origin after the callback returns.
Completion (parameter) sparkles.event_horizon.op.Completion donedone) nothrow @nogc
{
auto (local variable) eh_callback_tcp_echo.Endpoint* endpointendpoint = cast((struct) eh_callback_tcp_echo.EndpointEndpoint*) (parameter) void* contextcontext;
assert((parameter) sparkles.event_horizon.op.Completion donedone.(field) int sparkles.event_horizon.op.Completion.resraw result: >= 0 payload or -errno
res > 0 && (parameter) sparkles.event_horizon.op.Completion donedone.(field) int sparkles.event_horizon.op.Completion.resraw result: >= 0 payload or -errno
res <= (local variable) eh_callback_tcp_echo.Endpoint* endpointendpoint.(field) @system ulong eh_callback_tcp_echo.Endpoint.pendingSendpendingSend);
(local variable) eh_callback_tcp_echo.Endpoint* endpointendpoint.(field) @system ulong eh_callback_tcp_echo.Endpoint.sentsent += (parameter) sparkles.event_horizon.op.Completion donedone.(field) int sparkles.event_horizon.op.Completion.resraw result: >= 0 payload or -errno
res;
if ((local variable) eh_callback_tcp_echo.Endpoint* endpointendpoint.(field) @system ulong eh_callback_tcp_echo.Endpoint.sentsent < (constant) @system string eh_callback_tcp_echo.payload = "hello"payload.(constant) ulong "hello".length = 5LUlength) void eh_callback_tcp_echo.send(eh_callback_tcp_echo.Endpoint* endpoint) nothrow @nogc @systemsend((local variable) eh_callback_tcp_echo.Endpoint* endpointendpoint);
else if (!(local variable) eh_callback_tcp_echo.Endpoint* endpointendpoint.(field) @system bool eh_callback_tcp_echo.Endpoint.serverserver) void eh_callback_tcp_echo.receive(eh_callback_tcp_echo.Endpoint* endpoint) nothrow @nogc @systemreceive((local variable) eh_callback_tcp_echo.Endpoint* endpointendpoint);
}
void void eh_callback_tcp_echo.received(void* context, ref sparkles.event_horizon.op.Completion done) nothrow @nogc @systemreceived(void* (parameter) void* contextcontext, ref (struct) sparkles.event_horizon.op.CompletionWhat a tier-A callback receives. Passed by ref: the callback may
move(done.buf) to keep the buffer; otherwise the loop recycles it to its
origin after the callback returns.
Completion (parameter) sparkles.event_horizon.op.Completion donedone) nothrow @nogc
{
auto (local variable) eh_callback_tcp_echo.Endpoint* endpointendpoint = cast((struct) eh_callback_tcp_echo.EndpointEndpoint*) (parameter) void* contextcontext;
assert((parameter) sparkles.event_horizon.op.Completion donedone.(field) int sparkles.event_horizon.op.Completion.resraw result: >= 0 payload or -errno
res > 0 && (local variable) eh_callback_tcp_echo.Endpoint* endpointendpoint.(field) @system ulong eh_callback_tcp_echo.Endpoint.receivedreceived + (parameter) sparkles.event_horizon.op.Completion donedone.(field) int sparkles.event_horizon.op.Completion.resraw result: >= 0 payload or -errno
res <= (constant) @system string eh_callback_tcp_echo.payload = "hello"payload.(constant) ulong "hello".length = 5LUlength);
(local variable) eh_callback_tcp_echo.Endpoint* endpointendpoint.(field) @system char[5] eh_callback_tcp_echo.Endpoint.bytesbytes[(local variable) eh_callback_tcp_echo.Endpoint* endpointendpoint.(field) @system ulong eh_callback_tcp_echo.Endpoint.receivedreceived .. (local variable) eh_callback_tcp_echo.Endpoint* endpointendpoint.(field) @system ulong eh_callback_tcp_echo.Endpoint.receivedreceived + (parameter) sparkles.event_horizon.op.Completion donedone.(field) int sparkles.event_horizon.op.Completion.resraw result: >= 0 payload or -errno
res] = cast(const(char)[]) (parameter) sparkles.event_horizon.op.Completion donedone.(field) sparkles.event_horizon.buffer.Buf sparkles.event_horizon.op.Completion.bufthe buffer moving back out (may be empty)
buf[];
(local variable) eh_callback_tcp_echo.Endpoint* endpointendpoint.(field) @system ulong eh_callback_tcp_echo.Endpoint.receivedreceived += (parameter) sparkles.event_horizon.op.Completion donedone.(field) int sparkles.event_horizon.op.Completion.resraw result: >= 0 payload or -errno
res;
if ((local variable) eh_callback_tcp_echo.Endpoint* endpointendpoint.(field) @system ulong eh_callback_tcp_echo.Endpoint.receivedreceived < (constant) @system string eh_callback_tcp_echo.payload = "hello"payload.(constant) ulong "hello".length = 5LUlength) void eh_callback_tcp_echo.receive(eh_callback_tcp_echo.Endpoint* endpoint) nothrow @nogc @systemreceive((local variable) eh_callback_tcp_echo.Endpoint* endpointendpoint);
else
{
assert((local variable) eh_callback_tcp_echo.Endpoint* endpointendpoint.(field) @system char[5] eh_callback_tcp_echo.Endpoint.bytesbytes[] == (constant) @system string eh_callback_tcp_echo.payload = "hello"payload);
if ((local variable) eh_callback_tcp_echo.Endpoint* endpointendpoint.(field) @system bool eh_callback_tcp_echo.Endpoint.serverserver) void eh_callback_tcp_echo.send(eh_callback_tcp_echo.Endpoint* endpoint) nothrow @nogc @systemsend((local variable) eh_callback_tcp_echo.Endpoint* endpointendpoint);
}
}
void void eh_callback_tcp_echo.accepted(void* context, ref sparkles.event_horizon.op.Completion done) nothrow @nogc @systemaccepted(void* (parameter) void* contextcontext, ref (struct) sparkles.event_horizon.op.CompletionWhat a tier-A callback receives. Passed by ref: the callback may
move(done.buf) to keep the buffer; otherwise the loop recycles it to its
origin after the callback returns.
Completion (parameter) sparkles.event_horizon.op.Completion donedone) nothrow @nogc
{
auto (local variable) eh_callback_tcp_echo.Endpoint* endpointendpoint = cast((struct) eh_callback_tcp_echo.EndpointEndpoint*) (parameter) void* contextcontext;
assert((parameter) sparkles.event_horizon.op.Completion donedone.(field) int sparkles.event_horizon.op.Completion.resraw result: >= 0 payload or -errno
res >= 0);
(local variable) eh_callback_tcp_echo.Endpoint* endpointendpoint.(field) @system int eh_callback_tcp_echo.Endpoint.fdfd = (parameter) sparkles.event_horizon.op.Completion donedone.(field) int sparkles.event_horizon.op.Completion.resraw result: >= 0 payload or -errno
res;
void eh_callback_tcp_echo.receive(eh_callback_tcp_echo.Endpoint* endpoint) nothrow @nogc @systemreceive((local variable) eh_callback_tcp_echo.Endpoint* endpointendpoint);
}
void void eh_callback_tcp_echo.connected(void* context, ref sparkles.event_horizon.op.Completion done) nothrow @nogc @systemconnected(void* (parameter) void* contextcontext, ref (struct) sparkles.event_horizon.op.CompletionWhat a tier-A callback receives. Passed by ref: the callback may
move(done.buf) to keep the buffer; otherwise the loop recycles it to its
origin after the callback returns.
Completion (parameter) sparkles.event_horizon.op.Completion donedone) nothrow @nogc
{
assert((parameter) sparkles.event_horizon.op.Completion donedone.(field) int sparkles.event_horizon.op.Completion.resraw result: >= 0 payload or -errno
res == 0);
void eh_callback_tcp_echo.send(eh_callback_tcp_echo.Endpoint* endpoint) nothrow @nogc @systemsend(cast((struct) eh_callback_tcp_echo.EndpointEndpoint*) (parameter) void* contextcontext);
}
void void D main() @systemmain()
{
auto (local variable) int listenerlistener = 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);
assert((local variable) int listenerlistener >= 0);
scope(exit) int core.sys.posix.unistd.close(int) nothrow @nogc @trustedclose((local variable) int listenerlistener);
(struct) core.sys.posix.netinet.in_.sockaddr_insockaddr_in (local variable) core.sys.posix.netinet.in_.sockaddr_in addressaddress;
(local variable) core.sys.posix.netinet.in_.sockaddr_in addressaddress.(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 addressaddress.(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((enum value) core.sys.posix.netinet.in_.INADDR_LOOPBACK = 2130706433uINADDR_LOOPBACK);
assert(int core.sys.posix.sys.socket.bind(int, scope const(core.sys.posix.sys.socket.sockaddr*), uint) nothrow @nogcbind((local variable) int listenerlistener, cast((struct) core.sys.posix.sys.socket.sockaddrsockaddr*) &(local variable) core.sys.posix.netinet.in_.sockaddr_in addressaddress, (local variable) core.sys.posix.netinet.in_.sockaddr_in addressaddress.(constant) ulong core.sys.posix.netinet.in_.sockaddr_in.sizeof = 16LUsizeof) == 0);
assert(int core.sys.posix.sys.socket.listen(int, int) nothrow @nogc @safelisten((local variable) int listenerlistener, 1) == 0);
(alias) core.sys.posix.sys.socket.socklen_t = uintsocklen_t (local variable) uint lengthlength = (local variable) core.sys.posix.netinet.in_.sockaddr_in addressaddress.(constant) ulong core.sys.posix.netinet.in_.sockaddr_in.sizeof = 16LUsizeof;
assert(int core.sys.posix.sys.socket.getsockname(int, scope core.sys.posix.sys.socket.sockaddr*, scope uint*) nothrow @nogcgetsockname((local variable) int listenerlistener, cast((struct) core.sys.posix.sys.socket.sockaddrsockaddr*) &(local variable) core.sys.posix.netinet.in_.sockaddr_in addressaddress, &(local variable) uint lengthlength) == 0);
auto (local variable) int clientFdclientFd = 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);
assert((local variable) int clientFdclientFd >= 0);
scope(exit) int core.sys.posix.unistd.close(int) nothrow @nogc @trustedclose((local variable) int clientFdclientFd);
(struct) sparkles.event_horizon.buffer.BufferPool!(std.experimental.allocator.mallocator.Mallocator)Fixed pool of same-size buffers carved out of one contiguous slab drawn from
Allocator (SPEC §6.3) — the guideline-§4 generic-library shape: monostate
allocators cost zero bytes (stateSize idiom), stateful ones are passed to
create and embedded. When the backend supports registered buffers the slab
is registered once and every Buf carries its slot index; lowering then
picks the FIXED opcodes purely from Buf.origin — the user never spells
"fixed". (Registration wiring lands in M8; until then every pool Buf has
BufOrigin.pool.)
BufferPool!() (local variable) sparkles.event_horizon.buffer.BufferPool!(Mallocator) poolpool;
assert(!(template instance) sparkles.event_horizon.buffer.BufferPool!(std.experimental.allocator.mallocator.Mallocator)BufferPool!().expected.Expected!(void, IoError, NoGcHook) sparkles.event_horizon.buffer.BufferPool!(std.experimental.allocator.mallocator.Mallocator).create(out sparkles.event_horizon.buffer.BufferPool!(Mallocator) pool, uint count, uint bufSize) pure nothrow @nogc @safeAllocates count` × `bufSize bytes; all-or-nothing.
create((local variable) sparkles.event_horizon.buffer.BufferPool!(Mallocator) poolpool, 4, 2).bool expected.Expected!(void, sparkles.base.io.errors.IoError, sparkles.base.text.errors.NoGcHook).hasError!()() const pure nothrow @nogc @property @safeChecks if Expected has error
hasError);
(struct) sparkles.event_horizon.loop.EventLoop!(UringBackend, Mallocator)DefaultLoop (local variable) sparkles.event_horizon.loop.EventLoop!(UringBackend, Mallocator) looploop;
assert(!(struct) sparkles.event_horizon.loop.EventLoop!(UringBackend, Mallocator)DefaultLoop.expected.Expected!(void, IoError, NoGcHook) sparkles.event_horizon.loop.EventLoop!(sparkles.event_horizon.backend.uring.UringBackend, std.experimental.allocator.mallocator.Mallocator).create(out sparkles.event_horizon.loop.EventLoop!(UringBackend, Mallocator) loop, in sparkles.event_horizon.loop.LoopConfig cfg = LoopConfig(BackendConfig(256u, 0u, cast(ubyte)0u, cast(ubyte)0u), 0u, & defaultFatalHook)) nothrow @nogc @safeOut-parameter factory (SPEC §9.1: IoResult cannot return a
non-copyable owner by value): backend setup → capability probe (the
hard-error semantics of SPEC §3.4) → slab allocation. All-or-nothing.
create((local variable) sparkles.event_horizon.loop.EventLoop!(UringBackend, Mallocator) looploop).noreturn sparkles.event_horizon.loop.defaultFatalHook(in sparkles.base.io.errors.IoError why) nothrow @nogc @trustedThe default FatalHook: raw write(2) of a static message plus the
errno, then abort().
hasError, "no completion backend");
scope(exit) (local variable) sparkles.event_horizon.loop.EventLoop!(UringBackend, Mallocator) looploop.void sparkles.event_horizon.loop.EventLoop!(sparkles.event_horizon.backend.uring.UringBackend, std.experimental.allocator.mallocator.Mallocator).destroy() nothrow @nogc @safeStructured teardown; the scope discipline (M5) guarantees the
precondition at higher tiers. An armed waker (SPEC §5.6) is loop
infrastructure, not work — destroy disarms it itself. A queued or
in-flight cancellation is work: its target is live, so the first
precondition already covers the pending set and the reserved slot.
destroy();
(struct) eh_callback_tcp_echo.EndpointEndpoint (local variable) eh_callback_tcp_echo.Endpoint serverserver = (struct) eh_callback_tcp_echo.EndpointEndpoint(&(local variable) sparkles.event_horizon.loop.EventLoop!(UringBackend, Mallocator) looploop, &(local variable) sparkles.event_horizon.buffer.BufferPool!(Mallocator) poolpool, -1, true);
(struct) eh_callback_tcp_echo.EndpointEndpoint (local variable) eh_callback_tcp_echo.Endpoint clientclient = (struct) eh_callback_tcp_echo.EndpointEndpoint(&(local variable) sparkles.event_horizon.loop.EventLoop!(UringBackend, Mallocator) looploop, &(local variable) sparkles.event_horizon.buffer.BufferPool!(Mallocator) poolpool, (local variable) int clientFdclientFd);
scope(exit) if ((local variable) eh_callback_tcp_echo.Endpoint serverserver.(field) @system int eh_callback_tcp_echo.Endpoint.fdfd >= 0) int core.sys.posix.unistd.close(int) nothrow @nogc @trustedclose((local variable) eh_callback_tcp_echo.Endpoint serverserver.(field) @system int eh_callback_tcp_echo.Endpoint.fdfd);
(struct) sparkles.event_horizon.net.SockAddrA sockaddr_storage-sized POD plus its length — library-owned (SPEC
§4.1): peer backends and test doubles use it without any libc coupling.
SockAddr (local variable) sparkles.event_horizon.net.SockAddr targettarget;
(local variable) sparkles.event_horizon.net.SockAddr targettarget.(field) ubyte[128] sparkles.event_horizon.net.SockAddr.storageraw sockaddr bytes (or an opaque test-double key)
storage[0 .. (local variable) core.sys.posix.netinet.in_.sockaddr_in addressaddress.(constant) ulong core.sys.posix.netinet.in_.sockaddr_in.sizeof = 16LUsizeof] = (cast(ubyte*) &(local variable) core.sys.posix.netinet.in_.sockaddr_in addressaddress)[0 .. (local variable) core.sys.posix.netinet.in_.sockaddr_in addressaddress.(constant) ulong core.sys.posix.netinet.in_.sockaddr_in.sizeof = 16LUsizeof];
(local variable) sparkles.event_horizon.net.SockAddr targettarget.(field) uint sparkles.event_horizon.net.SockAddr.lenvalid length of storage
len = (local variable) core.sys.posix.netinet.in_.sockaddr_in addressaddress.(constant) ulong core.sys.posix.netinet.in_.sockaddr_in.sizeof = 16LUsizeof;
assert((local variable) sparkles.event_horizon.loop.EventLoop!(UringBackend, Mallocator) looploop.expected.Expected!(OpHandle, IoError, NoGcHook) sparkles.event_horizon.loop.EventLoop!(sparkles.event_horizon.backend.uring.UringBackend, std.experimental.allocator.mallocator.Mallocator).submit!(sparkles.event_horizon.op.OpAccept)(sparkles.event_horizon.op.OpAccept op, void function(void* ctx, ref sparkles.event_horizon.op.Completion done) nothrow @nogc cb, void* ctx = null) nothrow @nogc @safeSubmits op with a completion callback (SPEC §5.2). Owned buffers move
into the op slot and come back via Completion.buf; on a submission
error the buffer is recycled to its origin (it does not come back —
there will be no completion).
The ctx lifetime rule: ctx must stay valid until the op's
terminal completion is dispatched — cancel alone does not shorten
that (the terminal CQE still arrives and still invokes cb). A caller
whose ctx dies earlier must detach the handle first: after detach
returns, the callback never runs. Handing a stack frame's address to
ctx without one of those guarantees is exactly the use-after-free the
retired kernel-timer deadline shipped (SPEC §8.3).
Backpressure
on a full submission queue the loop performs one implicit
flush retry, then returns EAGAIN; an exhausted slab returns
ENOBUFS.
submit((struct) sparkles.event_horizon.op.OpAcceptAccept one connection; the completion res is the new fd. The peer
address is fetched on demand (getpeername), not stored per slot.
OpAccept((local variable) int listenerlistener), &void eh_callback_tcp_echo.accepted(void* context, ref sparkles.event_horizon.op.Completion done) nothrow @nogc @systemaccepted, &(local variable) eh_callback_tcp_echo.Endpoint serverserver).bool expected.Expected!(sparkles.event_horizon.op.OpHandle, sparkles.base.io.errors.IoError, sparkles.base.text.errors.NoGcHook).hasValue!()() const pure nothrow @nogc @property @safeChecks if Expected has value
hasValue);
assert((local variable) sparkles.event_horizon.loop.EventLoop!(UringBackend, Mallocator) looploop.expected.Expected!(OpHandle, IoError, NoGcHook) sparkles.event_horizon.loop.EventLoop!(sparkles.event_horizon.backend.uring.UringBackend, std.experimental.allocator.mallocator.Mallocator).submit!(sparkles.event_horizon.op.OpConnect)(sparkles.event_horizon.op.OpConnect op, void function(void* ctx, ref sparkles.event_horizon.op.Completion done) nothrow @nogc cb, void* ctx = null) nothrow @nogc @safeSubmits op with a completion callback (SPEC §5.2). Owned buffers move
into the op slot and come back via Completion.buf; on a submission
error the buffer is recycled to its origin (it does not come back —
there will be no completion).
The ctx lifetime rule: ctx must stay valid until the op's
terminal completion is dispatched — cancel alone does not shorten
that (the terminal CQE still arrives and still invokes cb). A caller
whose ctx dies earlier must detach the handle first: after detach
returns, the callback never runs. Handing a stack frame's address to
ctx without one of those guarantees is exactly the use-after-free the
retired kernel-timer deadline shipped (SPEC §8.3).
Backpressure
on a full submission queue the loop performs one implicit
flush retry, then returns EAGAIN; an exhausted slab returns
ENOBUFS.
submit((struct) sparkles.event_horizon.op.OpConnectOutbound connect; the address is copied into the operand store.
OpConnect((local variable) int clientFdclientFd, (local variable) sparkles.event_horizon.net.SockAddr targettarget), &void eh_callback_tcp_echo.connected(void* context, ref sparkles.event_horizon.op.Completion done) nothrow @nogc @systemconnected, &(local variable) eh_callback_tcp_echo.Endpoint clientclient).bool expected.Expected!(sparkles.event_horizon.op.OpHandle, sparkles.base.io.errors.IoError, sparkles.base.text.errors.NoGcHook).hasValue!()() const pure nothrow @nogc @property @safeChecks if Expected has value
hasValue);
assert(!(local variable) sparkles.event_horizon.loop.EventLoop!(UringBackend, Mallocator) looploop.expected.Expected!(void, IoError, NoGcHook) sparkles.event_horizon.loop.EventLoop!(sparkles.event_horizon.backend.uring.UringBackend, std.experimental.allocator.mallocator.Mallocator).run() nothrow @nogc @safeRuns until stop() or until drained (no live user/timer ops — an
armed waker alone does not keep ``run() alive; SPEC §5.6).
run().bool expected.Expected!(void, sparkles.base.io.errors.IoError, sparkles.base.text.errors.NoGcHook).hasError!()() const pure nothrow @nogc @property @safeChecks if Expected has error
hasError);
assert((local variable) sparkles.event_horizon.loop.EventLoop!(UringBackend, Mallocator) looploop.uint sparkles.event_horizon.loop.EventLoop!(sparkles.event_horizon.backend.uring.UringBackend, std.experimental.allocator.mallocator.Mallocator).inFlight() const pure nothrow @nogc @safeUser and timer ops currently in flight (an armed waker is excluded —
infrastructure, not work; SPEC §5.6).
inFlight == 0 && (local variable) sparkles.event_horizon.buffer.BufferPool!(Mallocator) poolpool.uint sparkles.event_horizon.buffer.BufferPool!(std.experimental.allocator.mallocator.Mallocator).available() const pure nothrow @nogc @safeBuffers currently available.
available == 4);
assert((local variable) eh_callback_tcp_echo.Endpoint serverserver.(field) @system ulong eh_callback_tcp_echo.Endpoint.sentsent == 5 && (local variable) eh_callback_tcp_echo.Endpoint clientclient.(field) @system ulong eh_callback_tcp_echo.Endpoint.sentsent == 5);
assert((local variable) eh_callback_tcp_echo.Endpoint serverserver.(field) @system ulong eh_callback_tcp_echo.Endpoint.receivedreceived == 5 && (local variable) eh_callback_tcp_echo.Endpoint clientclient.(field) @system ulong eh_callback_tcp_echo.Endpoint.receivedreceived == 5);
assert((local variable) eh_callback_tcp_echo.Endpoint clientclient.(field) @system char[5] eh_callback_tcp_echo.Endpoint.bytesbytes[] == (constant) @system string eh_callback_tcp_echo.payload = "hello"payload);
void std.stdio.writeln!(string, char[])(string __param_0, char[] __param_1) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln("echoed: ", (local variable) eh_callback_tcp_echo.Endpoint clientclient.(field) @system char[5] eh_callback_tcp_echo.Endpoint.bytesbytes[]);
}