eh_spawn.dhover×70all
#!/usr/bin/env dub
/+ dub.sdl:
    name "eh_spawn"
    dependency "sparkles:event-horizon" path="../../../../.."
    buildType "checked" {
        buildOptions "optimize" "inline" "debugInfo"
    }
    platforms "linux"
+/
import 
(package) core
core
.
(module) core.time

Module 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

@copyrightCopyright 2010 - 2012@licenseBoost License 1.0.@authorsJonathan M Davis and Kato Shoichi
time
: msecs;
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_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);
    }
}
@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.SupervisedProcessConfig

Knobs 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 cfg
cfg
;
(local variable) sparkles.event_horizon.proc.SupervisedProcessConfig cfg
cfg
.
(field) core.time.Duration sparkles.event_horizon.proc.SupervisedProcessConfig.terminateGrace

TERM → grace → KILL

terminateGrace
= 500.msecs; // cancellation: TERM, then bounded KILL grace
(local variable) sparkles.event_horizon.proc.SupervisedProcessConfig cfg
cfg
.
(field) sparkles.event_horizon.proc.ProcessConfig sparkles.event_horizon.proc.SupervisedProcessConfig.process
process
.
(field) sparkles.event_horizon.proc.StdioSpec sparkles.event_horizon.proc.ProcessConfig.stdinSpec
stdinSpec
=
(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.nullDev = 2

/dev/null

nullDev
);
int
(local variable) int linesSeen
linesSeen
;
(struct) sparkles.event_horizon.proc.SupervisedProcessResult

The 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 result
result
;
bool
(local variable) bool returned
returned
;
auto
(local variable) expected.Expected!(void, Cause!(IoError), NoGcHook) scopeResult
scopeResult
=
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)) @system

Runs 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 s
s
);
assert(!
(local variable) expected.Expected!(void, Cause!(IoError), NoGcHook) scopeResult
scopeResult
.
bool expected.Expected!(void, sparkles.event_horizon.cause.Cause!(IoError), sparkles.base.text.errors.NoGcHook).hasError!()() const pure nothrow @nogc @property @safe

Checks if Expected has error

hasError
&&
(local variable) bool returned
returned
);
assert(
(local variable) int linesSeen
linesSeen
== 2);
assert(
(local variable) sparkles.event_horizon.proc.SupervisedProcessResult result
result
.
(field) sparkles.event_horizon.proc.ProcessEnd sparkles.event_horizon.proc.SupervisedProcessResult.end
end
==
(enum) sparkles.event_horizon.proc.ProcessEnd

How 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 = 2

the surrounding scope was cancelled first

cancelled
);
assert(
(local variable) sparkles.event_horizon.proc.SupervisedProcessResult result
result
.
(field) sparkles.event_horizon.proc.ReapOutcome sparkles.event_horizon.proc.SupervisedProcessResult.reap

status is valid ONLY when ``reap == reaped

reap
==
(enum) sparkles.event_horizon.proc.ReapOutcome

Whether 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 = 1

the status was consumed here; status is valid

reaped
);
assert(
(local variable) sparkles.event_horizon.proc.SupervisedProcessResult result
result
.
(field) sparkles.event_horizon.proc.ExitStatus sparkles.event_horizon.proc.SupervisedProcessResult.status

valid only when reap == ReapOutcome.reaped

status
.
(field) bool sparkles.event_horizon.proc.ExitStatus.signaled

killed by a signal rather than exiting

signaled
&&
(local variable) sparkles.event_horizon.proc.SupervisedProcessResult result
result
.
(field) sparkles.event_horizon.proc.ExitStatus sparkles.event_horizon.proc.SupervisedProcessResult.status

valid only when reap == ReapOutcome.reaped

status
.
(field) int sparkles.event_horizon.proc.ExitStatus.code

exit code, or the signal number when signaled

code
== 15);
assert(!
(local variable) sparkles.event_horizon.proc.SupervisedProcessResult result
result
.
(field) bool sparkles.event_horizon.proc.SupervisedProcessResult.eofForced

a 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) @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
("end: ",
(local variable) sparkles.event_horizon.proc.SupervisedProcessResult result
result
.
(field) sparkles.event_horizon.proc.ProcessEnd sparkles.event_horizon.proc.SupervisedProcessResult.end
end
, ", reap: ",
(local variable) sparkles.event_horizon.proc.SupervisedProcessResult result
result
.
(field) sparkles.event_horizon.proc.ReapOutcome sparkles.event_horizon.proc.SupervisedProcessResult.reap

status is valid ONLY when ``reap == reaped

reap
);
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
);
}