#!/usr/bin/env dub
/+ dub.sdl:
name "eh_spawn"
dependency "sparkles:event-horizon" path="../../../../.."
buildType "checked" {
buildOptions "optimize" "inline" "debugInfo"
}
platforms "linux"
+/
import (package) corecore.(module) core.timeModule containing core time functionality, such as Duration (which
represents a duration of time) or MonoTime (which represents a
timestamp of the system's monotonic clock).
Various functions take a string (or strings) to represent a unit of time
(e.g. convert!("days", "hours")(numDays)). The valid strings to use
with such functions are "years", "months", "weeks", "days", "hours",
"minutes", "seconds", "msecs" (milliseconds), "usecs" (microseconds),
"hnsecs" (hecto-nanoseconds - i.e. 100 ns) or some subset thereof. There
are a few functions that also allow "nsecs", but very little actually
has precision greater than hnsecs.
Symbol Description Types Duration Represents a duration of time of weeks or less (kept internally as hnsecs). (e.g. 22 days or 700 seconds). TickDuration DEPRECATED Represents a duration of time in system clock ticks, using the highest precision that the system provides. MonoTime Represents a monotonic timestamp in system clock ticks, using the highest precision that the system provides. Functions convert Generic way of converting between two time units. dur Allows constructing a Duration from the given time units with the given length. weeks days hours
minutes seconds msecs
usecs hnsecs nsecs |
Convenience aliases for dur. |
| abs | Returns the absolute value of a duration. |
From Duration
From TickDuration
From units
To Duration tickDuration.to, std,conv!Duration() dur!"msecs"(5) or 5.msecs()
| To TickDuration |
duration.to, std,conv!TickDuration() |
|
TickDuration.from!"msecs"(msecs) |
| To units |
duration.total!"days" |
tickDuration.msecs |
convert!("days", "msecs")(msecs) |
Source
core/time.d
time : msecs;
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_spawn.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.SupervisedProcessConfigKnobs of one supervised run (SPEC §13.5). Zero timeout means no
deadline; zero sampleInterval means final accounting only. Every
buffer the run keeps is bounded: maxLineBytes caps one framed line and
maxCapturedBytes caps each raw collection — overflow is reported in the
events and the result (SPEC §13.6), never silent.
SupervisedProcessConfig (local variable) sparkles.event_horizon.proc.SupervisedProcessConfig cfgcfg;
(local variable) sparkles.event_horizon.proc.SupervisedProcessConfig cfgcfg.(field) core.time.Duration sparkles.event_horizon.proc.SupervisedProcessConfig.terminateGraceTERM → grace → KILL
terminateGrace = 500.msecs; // cancellation: TERM, then bounded KILL grace
(local variable) sparkles.event_horizon.proc.SupervisedProcessConfig cfgcfg.(field) sparkles.event_horizon.proc.ProcessConfig sparkles.event_horizon.proc.SupervisedProcessConfig.processprocess.(field) sparkles.event_horizon.proc.StdioSpec sparkles.event_horizon.proc.ProcessConfig.stdinSpecstdinSpec = (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.nullDev = 2/dev/null
nullDev);
int (local variable) int linesSeenlinesSeen;
(struct) sparkles.event_horizon.proc.SupervisedProcessResultThe outcome of one supervised run (SPEC §13.5). stdout_/stderr_
hold the exact raw bytes including original terminators, independently
of the normalized line events; empty unless that stream was piped, and
cut at maxCapturedBytes with the matching flag set.
SupervisedProcessResult (local variable) sparkles.event_horizon.proc.SupervisedProcessResult resultresult;
bool (local variable) bool returnedreturned;
auto (local variable) expected.Expected!(void, Cause!(IoError), NoGcHook) scopeResultscopeResult = expected.Expected!(void, Cause!(IoError), NoGcHook) eh_spawn.main.__lambda_L21_C26.withScope!(sparkles.event_horizon.sched.Sched)(ref sparkles.event_horizon.sched.Sched exec, sparkles.event_horizon.scope_.ScopeOptions opts = ScopeOptions(OnChildFailure.cancelSiblings)) @systemRuns fn(ref Scope) with a fresh child scope (SPEC §8.1). The calling
fiber counts as a member for cancellation targeting (a deadline or cancel
sweeping the scope interrupts the body's own awaits); the join runs after
the body returns, and is itself uncancellable.
Returns Outcome!(T, E) where T is fn's return type: the body's value,
or the scope's first cause.
withScope!((ref outer) {
JoinHandle!void task;
outer.fork(task, () {
auto got = supervise(s, ["sh", "-c", "echo one; echo two; exec sleep 30"], cfg,
null, (in ProcessEvent ev) {
final switch (ev.kind)
{
case ProcessEventKind.line:
assert(cast(const(char)[]) ev.line.bytes == (linesSeen == 0 ? "one" : "two"));
writeln("line: ", cast(const(char)[]) ev.line.bytes);
++linesSeen;
if (linesSeen == 2) outer.cancel(); // readiness handshake, no startup-time race
break;
case ProcessEventKind.exited:
writeln("exited: ", ev.end, ", signaled by ",
ev.status.signaled ? ev.status.code : 0);
break;
case ProcessEventKind.sample:
break; // periodic resource samples; not printed here
}
});
assert(got.hasValue);
result = got.value;
returned = true;
return ioOk();
});
task.join(s);
})((local variable) sparkles.event_horizon.sched.Sched ss);
assert(!(local variable) expected.Expected!(void, Cause!(IoError), NoGcHook) scopeResultscopeResult.bool expected.Expected!(void, sparkles.event_horizon.cause.Cause!(IoError), sparkles.base.text.errors.NoGcHook).hasError!()() const pure nothrow @nogc @property @safeChecks if Expected has error
hasError && (local variable) bool returnedreturned);
assert((local variable) int linesSeenlinesSeen == 2);
assert((local variable) sparkles.event_horizon.proc.SupervisedProcessResult resultresult.(field) sparkles.event_horizon.proc.ProcessEnd sparkles.event_horizon.proc.SupervisedProcessResult.endend == (enum) sparkles.event_horizon.proc.ProcessEndHow a supervised run ended (SPEC §13.5). The first timeout/cancel trigger
wins; later triggers only advance cleanup (SPEC §13.7).
ProcessEnd.(enum value) sparkles.event_horizon.proc.ProcessEnd.cancelled = 2the surrounding scope was cancelled first
cancelled);
assert((local variable) sparkles.event_horizon.proc.SupervisedProcessResult resultresult.(field) sparkles.event_horizon.proc.ReapOutcome sparkles.event_horizon.proc.SupervisedProcessResult.reapstatus is valid ONLY when ``reap == reaped
reap == (enum) sparkles.event_horizon.proc.ReapOutcomeWhether the supervisor consumed the root's exit status (SPEC §13.7).
exited is the terminal-child event, not proof that THIS supervisor
reaped: a concurrent external reaper (a contract violation the run
survives) leaves status invalid, self-described by this field.
ReapOutcome.(enum value) sparkles.event_horizon.proc.ReapOutcome.reaped = 1the status was consumed here; status is valid
reaped);
assert((local variable) sparkles.event_horizon.proc.SupervisedProcessResult resultresult.(field) sparkles.event_horizon.proc.ExitStatus sparkles.event_horizon.proc.SupervisedProcessResult.statusvalid only when reap == ReapOutcome.reaped
status.(field) bool sparkles.event_horizon.proc.ExitStatus.signaledkilled by a signal rather than exiting
signaled && (local variable) sparkles.event_horizon.proc.SupervisedProcessResult resultresult.(field) sparkles.event_horizon.proc.ExitStatus sparkles.event_horizon.proc.SupervisedProcessResult.statusvalid only when reap == ReapOutcome.reaped
status.(field) int sparkles.event_horizon.proc.ExitStatus.codeexit code, or the signal number when signaled
code == 15);
assert(!(local variable) sparkles.event_horizon.proc.SupervisedProcessResult resultresult.(field) bool sparkles.event_horizon.proc.SupervisedProcessResult.eofForceda still-open pipe was forced closed at a drain window
eofForced);
void std.stdio.writeln!(string, sparkles.event_horizon.proc.ProcessEnd, string, sparkles.event_horizon.proc.ReapOutcome)(string __param_0, sparkles.event_horizon.proc.ProcessEnd __param_1, string __param_2, sparkles.event_horizon.proc.ReapOutcome __param_3) @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("end: ", (local variable) sparkles.event_horizon.proc.SupervisedProcessResult resultresult.(field) sparkles.event_horizon.proc.ProcessEnd sparkles.event_horizon.proc.SupervisedProcessResult.endend, ", reap: ", (local variable) sparkles.event_horizon.proc.SupervisedProcessResult resultresult.(field) sparkles.event_horizon.proc.ReapOutcome sparkles.event_horizon.proc.SupervisedProcessResult.reapstatus is valid ONLY when ``reap == reaped
reap);
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);
}