eh_exec.dhover×62all
#!/usr/bin/env dub
/+ dub.sdl:
    name "eh_exec"
    dependency "sparkles:event-horizon" path="../../../../.."
    buildType "checked" {
        buildOptions "optimize" "inline" "debugInfo"
    }
    platforms "linux"
+/
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) 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);
    }
}
@paramargs the items to write to stdout@throwsIn case of an I/O error, throws an StdioException.
writeln
;
import
(package) sparkles
sparkles
.
(module) sparkles.event_horizon

sparkles: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.LoopGroup

A running loop group. Non-copyable; owns its worker scheduler(s).

LoopGroup
(local variable) sparkles.event_horizon.group.LoopGroup group
group
;
if (
(struct) sparkles.event_horizon.group.LoopGroup

A 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 group
group
).
bool expected.Expected!(void, sparkles.base.io.errors.IoError, sparkles.base.text.errors.NoGcHook).hasError!()() const pure nothrow @nogc @property @safe

Checks if Expected has error

hasError
)
assert(false, "no completion backend"); scope (exit)
(local variable) sparkles.event_horizon.group.LoopGroup group
group
.
void sparkles.event_horizon.group.LoopGroup.shutdown(core.time.Duration grace = dur(5L)) nothrow @safe

Tears down the group's workers.

shutdown
();
auto
(local variable) expected.Expected!(int, Cause!(IoError), NoGcHook) run
run
=
(local variable) sparkles.event_horizon.group.LoopGroup group
group
.
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) @system

Runs 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) sc
sc
, 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) env
env
) {
ref
(struct) sparkles.event_horizon.sched.Sched

The 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 s
s
=
sparkles.event_horizon.sched.Sched sparkles.event_horizon.sched.currentScheduler() nothrow @nogc ref @trusted

The 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.ProcessConfig

How 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 cfg
cfg
;
(local variable) sparkles.event_horizon.proc.ProcessConfig cfg
cfg
.
(field) sparkles.event_horizon.proc.StdioSpec sparkles.event_horizon.proc.ProcessConfig.stderrSpec
stderrSpec
=
(struct) sparkles.event_horizon.proc.StdioSpec

One stream's destination.

StdioSpec
(
(enum) sparkles.event_horizon.proc.StdioMode

Where one of the child's standard streams goes.

StdioMode
.
(enum value) sparkles.event_horizon.proc.StdioMode.pipe = 1

a pipe whose parent end rides the returned handle

pipe
);
auto
(local variable) expected.Expected!(CapturedOutput, IoError, NoGcHook) got
got
=
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) @trusted

Runs 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 s
s
, ["sh", "-c", "echo out; echo err >&2; exit 3"],
(local variable) sparkles.event_horizon.proc.ProcessConfig cfg
cfg
);
assert(
(local variable) expected.Expected!(CapturedOutput, IoError, NoGcHook) got
got
.
bool expected.Expected!(sparkles.event_horizon.live.CapturedOutput, sparkles.base.io.errors.IoError, sparkles.base.text.errors.NoGcHook).hasValue!()() const pure nothrow @nogc @property @safe

Checks if Expected has value

hasValue
);
assert(
(local variable) expected.Expected!(CapturedOutput, IoError, NoGcHook) got
got
.
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 @safe

Returns 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.status
status
.
(field) int sparkles.event_horizon.proc.ExitStatus.code

exit code, or the signal number when signaled

code
== 3);
assert(cast(const(char)[])
(local variable) expected.Expected!(CapturedOutput, IoError, NoGcHook) got
got
.
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 @safe

Returns 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) got
got
.
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 @safe

Returns 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) @safe

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);
    }
}
@paramargs the items to write to stdout@throwsIn case of an I/O error, throws an StdioException.
writeln
("stdout: ", cast(const(char)[])
(local variable) expected.Expected!(CapturedOutput, IoError, NoGcHook) got
got
.
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 @safe

Returns 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) @safe

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);
    }
}
@paramargs the items to write to stdout@throwsIn case of an I/O error, throws an StdioException.
writeln
("stderr: ", cast(const(char)[])
(local variable) expected.Expected!(CapturedOutput, IoError, NoGcHook) got
got
.
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 @safe

Returns 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) @safe

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);
    }
}
@paramargs the items to write to stdout@throwsIn case of an I/O error, throws an StdioException.
writeln
("exit code: ",
(local variable) expected.Expected!(CapturedOutput, IoError, NoGcHook) got
got
.
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 @safe

Returns 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.status
status
.
(field) int sparkles.event_horizon.proc.ExitStatus.code

exit code, or the signal number when signaled

code
);
return 0; }); assert(
(local variable) expected.Expected!(int, Cause!(IoError), NoGcHook) run
run
.
bool expected.Expected!(int, sparkles.event_horizon.cause.Cause!(IoError), sparkles.base.text.errors.NoGcHook).hasValue!()() const pure nothrow @nogc @property @safe

Checks if Expected has value

hasValue
);
}