#!/usr/bin/env dub
/+ dub.sdl:
name "eh_exec"
dependency "sparkles:event-horizon" path="../../../../.."
buildType "checked" {
buildOptions "optimize" "inline" "debugInfo"
}
platforms "linux"
+/
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_exec.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.(module) sparkles.event_horizonsparkles:event-horizon — the completion-first event loop with a native
algebraic-effect layer. This package module re-exports the public surface
(SPEC §16); import sparkles.event_horizon`` for the whole API, or a specific
module for a narrower dependency.
event_horizon;
void void D main()main()
{
(struct) sparkles.event_horizon.group.LoopGroupA running loop group. Non-copyable; owns its worker scheduler(s).
LoopGroup (local variable) sparkles.event_horizon.group.LoopGroup groupgroup;
if ((struct) sparkles.event_horizon.group.LoopGroupA running loop group. Non-copyable; owns its worker scheduler(s).
LoopGroup.expected.Expected!(void, IoError, NoGcHook) sparkles.event_horizon.group.LoopGroup.start(out sparkles.event_horizon.group.LoopGroup group, in sparkles.event_horizon.group.LoopGroupConfig cfg = LoopGroupConfig(Topology.single, 0u, 256u, Flag.yes, Flag.yes, 256u, Flag.no))Probes the kernel and starts the group. io_uring probe failure is a
hard error (SPEC §3.4 — no epoll fallback).
start((local variable) sparkles.event_horizon.group.LoopGroup groupgroup).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(false, "no completion backend");
scope (exit) (local variable) sparkles.event_horizon.group.LoopGroup groupgroup.void sparkles.event_horizon.group.LoopGroup.shutdown(core.time.Duration grace = dur(5L)) nothrow @safeTears down the group's workers.
shutdown();
auto (local variable) expected.Expected!(int, Cause!(IoError), NoGcHook) runrun = (local variable) sparkles.event_horizon.group.LoopGroup groupgroup.expected.Expected!(int, Cause!(IoError), NoGcHook) sparkles.event_horizon.group.LoopGroup.run!int(scope int delegate(ref sparkles.event_horizon.scope_.Scope!(Sched, IoError) root, ref sparkles.event_horizon.capability.Ctx!(RingClock, RingVfs, RingNet, RingProc) env) main) @systemRuns main as the root fiber inside the root scope, handing it the live
capability row. All authority originates here. Blocks the calling thread
until the root scope joins. The group remains available until explicit
shutdown or destruction.
run((ref (alias) sparkles.event_horizon.group.RootScope = sparkles.event_horizon.scope_.Scope!(Sched, IoError)The blessed live scope instantiation handed to the root fiber (SPEC §7.2,
§11): a Scope over the ring-backed Sched executor with the IoError
channel.
RootScope (parameter) sparkles.event_horizon.scope_.Scope!(Sched, IoError) scsc, ref (alias) sparkles.event_horizon.live.Env = sparkles.event_horizon.capability.Ctx!(RingClock, RingVfs, RingNet, RingProc)The default live capability row handed to the root fiber (SPEC §11).
fs is on every backend: RingVfs needs no ring file operations
(SPEC §10.5).
Env (parameter) sparkles.event_horizon.capability.Ctx!(RingClock, RingVfs, RingNet, RingProc) envenv) {
ref (struct) sparkles.event_horizon.sched.SchedThe per-thread fiber scheduler (SPEC §7.2). Owns its tier-A loop; the FIFO
ready queue is intrusive (no allocation); completion callbacks enqueue,
never resume inline.
Sched (local variable) sparkles.event_horizon.sched.Sched ss = sparkles.event_horizon.sched.Sched sparkles.event_horizon.sched.currentScheduler() nothrow @nogc ref @trustedThe scheduler driving the calling fiber.
The tier-B verbs already work this way — read, write, accept take no
scheduler because a fiber knows which loop resumed it. This exposes the same
answer to code that has to name one: waitReadable, capture, anything
taking ref Sched.
It exists because a daemon body cannot be handed a scheduler. An
application parks background work through its host's spawnDaemon, which
takes a plain void delegate() — the host deliberately does not publish the
loop, since two of the three targets have none. A fiber that needs the
scheduler therefore has to ask at the top, and this is the ask.
By ref, never by pointer. Every consumer wants ref Sched, so handing
back a pointer would make each of them dereference one — and a dereference in
@safe code is not free, it is a @trusted escape hatch per call site. A
ref return plus a ref local (ref Sched s = currentScheduler();) keeps
the whole chain @safe. The nullability that a pointer also carried moves to
onScheduler, where it reads as the question it always was.
currentScheduler();
(struct) sparkles.event_horizon.proc.ProcessConfigHow to spawn a child (SPEC §13.1). The defaults capture stdout and
leave stdin/stderr shared — the "run a tool, read its output" shape.
ProcessConfig (local variable) sparkles.event_horizon.proc.ProcessConfig cfgcfg;
(local variable) sparkles.event_horizon.proc.ProcessConfig cfgcfg.(field) sparkles.event_horizon.proc.StdioSpec sparkles.event_horizon.proc.ProcessConfig.stderrSpecstderrSpec = (struct) sparkles.event_horizon.proc.StdioSpecOne stream's destination.
StdioSpec((enum) sparkles.event_horizon.proc.StdioModeWhere one of the child's standard streams goes.
StdioMode.(enum value) sparkles.event_horizon.proc.StdioMode.pipe = 1a pipe whose parent end rides the returned handle
pipe);
auto (local variable) expected.Expected!(CapturedOutput, IoError, NoGcHook) gotgot = expected.Expected!(CapturedOutput, IoError, NoGcHook) sparkles.event_horizon.live.capture(ref sparkles.event_horizon.sched.Sched s, scope const(char[])[] argv, in sparkles.event_horizon.proc.ProcessConfig cfg = ProcessConfig(StdioSpec(StdioMode.inherit, -1), StdioSpec(StdioMode.pipe, -1), StdioSpec(StdioMode.inherit, -1), null, null, null, false), scope const(ubyte)[] stdinBytes = null) @trustedRuns argv to completion (SPEC §13.2): spawn per cfg, feed
stdinBytes (a non-null value upgrades a default inherit stdin to a
pipe), drain the piped outputs concurrently — parked reads, so a
chatty child can never deadlock against an undrained pipe (the hazard
runCaptured's temp files work around) — then reap.
The scope join is the lifetime argument: the drain fibers borrow this
frame's locals and are all joined before it returns.
No exit leaves a zombie. A drain fiber that cannot be admitted, a read
that fails, and a cancellation that interrupts the run each end the
child (SIGKILL) and reap it — on the shared pool's termination-critical
lane, under protect, so a latched interrupt is delivered at the
caller's next checkpoint and never between the kill and the reap. The
error returned is the first cause: the admission error, the read error,
or the interrupt as ECANCELED. A failed read is never mistaken for EOF.
capture((local variable) sparkles.event_horizon.sched.Sched ss, ["sh", "-c", "echo out; echo err >&2; exit 3"], (local variable) sparkles.event_horizon.proc.ProcessConfig cfgcfg);
assert((local variable) expected.Expected!(CapturedOutput, IoError, NoGcHook) gotgot.bool expected.Expected!(sparkles.event_horizon.live.CapturedOutput, 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) expected.Expected!(CapturedOutput, IoError, NoGcHook) gotgot.inout(sparkles.event_horizon.live.CapturedOutput) expected.Expected!(sparkles.event_horizon.live.CapturedOutput, 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.(field) sparkles.event_horizon.proc.ExitStatus sparkles.event_horizon.live.CapturedOutput.statusstatus.(field) int sparkles.event_horizon.proc.ExitStatus.codeexit code, or the signal number when signaled
code == 3);
assert(cast(const(char)[]) (local variable) expected.Expected!(CapturedOutput, IoError, NoGcHook) gotgot.inout(sparkles.event_horizon.live.CapturedOutput) expected.Expected!(sparkles.event_horizon.live.CapturedOutput, 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.(field) sparkles.base.buffer.Buffer!(ubyte, 256LU, cast(Storage)cast(ubyte)3u) sparkles.event_horizon.live.CapturedOutput.stdout_empty unless stdoutSpec piped
stdout_[] == "out\n");
assert(cast(const(char)[]) (local variable) expected.Expected!(CapturedOutput, IoError, NoGcHook) gotgot.inout(sparkles.event_horizon.live.CapturedOutput) expected.Expected!(sparkles.event_horizon.live.CapturedOutput, 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.(field) sparkles.base.buffer.Buffer!(ubyte, 256LU, cast(Storage)cast(ubyte)3u) sparkles.event_horizon.live.CapturedOutput.stderr_empty unless stderrSpec piped
stderr_[] == "err\n");
void std.stdio.writeln!(string, const(char)[])(string __param_0, const(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("stdout: ", cast(const(char)[]) (local variable) expected.Expected!(CapturedOutput, IoError, NoGcHook) gotgot.inout(sparkles.event_horizon.live.CapturedOutput) expected.Expected!(sparkles.event_horizon.live.CapturedOutput, 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.(field) sparkles.base.buffer.Buffer!(ubyte, 256LU, cast(Storage)cast(ubyte)3u) sparkles.event_horizon.live.CapturedOutput.stdout_empty unless stdoutSpec piped
stdout_[]);
void std.stdio.writeln!(string, const(char)[])(string __param_0, const(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("stderr: ", cast(const(char)[]) (local variable) expected.Expected!(CapturedOutput, IoError, NoGcHook) gotgot.inout(sparkles.event_horizon.live.CapturedOutput) expected.Expected!(sparkles.event_horizon.live.CapturedOutput, 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.(field) sparkles.base.buffer.Buffer!(ubyte, 256LU, cast(Storage)cast(ubyte)3u) sparkles.event_horizon.live.CapturedOutput.stderr_empty unless stderrSpec piped
stderr_[]);
void std.stdio.writeln!(string, int)(string __param_0, int __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("exit code: ", (local variable) expected.Expected!(CapturedOutput, IoError, NoGcHook) gotgot.inout(sparkles.event_horizon.live.CapturedOutput) expected.Expected!(sparkles.event_horizon.live.CapturedOutput, 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.(field) sparkles.event_horizon.proc.ExitStatus sparkles.event_horizon.live.CapturedOutput.statusstatus.(field) int sparkles.event_horizon.proc.ExitStatus.codeexit code, or the signal number when signaled
code);
return 0;
});
assert((local variable) expected.Expected!(int, Cause!(IoError), NoGcHook) runrun.bool expected.Expected!(int, sparkles.event_horizon.cause.Cause!(IoError), sparkles.base.text.errors.NoGcHook).hasValue!()() const pure nothrow @nogc @property @safeChecks if Expected has value
hasValue);
}