1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
//! **One child, from the fork to the capture**: the spawn, the poll that is
//! also the drain, and the cascade that stops a tool which will not stop
//! itself (DESIGN §3.5).
//!
//! **The deadline bounds the capture, not the child's exit** (bl-6c14). Those
//! are different things the moment a tool backgrounds anything: the helper
//! holds the same pipe write ends, so a drain that waited for the pipe to end
//! waited on a process this box never started, and the serial loop behind it
//! waited too — forever, on a tool that had already finished. The loop here
//! reads what is there rather than waiting for the end (`exec::pipes`), so
//! every path out of this file is inside the deadline plus the cascade's own
//! grace, and the bytes the tool produced come back either way.
use Stdio;
use ;
use Value;
use Pipes;
use ;
use crateLocal;
use crateCapture;
/// How long a child gets between being asked to stop and being stopped. Long
/// enough for a tool to flush a file or drop a lock, short enough that the
/// engine's own patience is not spent on a corpse.
const GRACE: Duration = from_secs;
/// How often a running child is looked at when nothing is moving. A latency
/// knob on the *answer*, not on the run: a tick that read something reads again
/// at once, so a loud tool is drained at the pipe's speed and never at this
/// one's.
const POLL: Duration = from_millis;
/// The spawn and the drain. The `Err` is a fork that never happened — an argv
/// with no program in it, a missing binary, an unusable working directory —
/// which is the one outcome that is not the child's own verdict, and which of
/// the three it was is [`unstarted`]'s to say. `subject` is the invocation's
/// own working directory when the entry consents (`exec::subject_cwd`); it
/// outranks the entry's `cwd`, which stands for every cwd-less invocation
/// exactly as before.
pub
/// **The fork that never happened, said about the thing that was actually
/// wrong** (bl-3c93).
///
/// A spawn fails because the program is not there, because the argv named
/// nothing, or because the working directory is unusable — and the operating
/// system spells all three `ENOENT`, so a sentence built from the program alone
/// names the wrong party two times in three. An operator reading *"`<the
/// command>`: No such file or directory"* about a command that is exactly right
/// goes looking at a file that is exactly right.
///
/// **The directory is looked at only after the failure.** A check before the
/// fork would stat on every invocation to answer a question that is almost
/// always "yes", and could refuse a spawn that would have worked; after it, the
/// look is free and the answer is the one the operator needs. It is the
/// *existence* half only — the shape half (a relative path) is the document's
/// and `config::one` refuses it at the read.
/// Wait for the child, draining it as it goes, or stop it. The second half of
/// the answer is what to say about the stopping — empty when the child answered
/// on its own, because a tool that finished has nothing this foot needs to add.
///
/// **The pump is inside the wait and not beside it.** A tool that writes more
/// than a pipe holds blocks until somebody reads, so the thing that watches the
/// clock has to be the thing that reads; and a tick that moved bytes goes round
/// again without sleeping, so the poll interval bounds how late an *answer* is
/// and never how fast output comes back.
/// **The cascade**: ask, wait, insist — and its subject is the child's process
/// GROUP, so a tool that started something does not leave that something behind
/// (bl-a78e). A tool that traps `SIGTERM` to flush a file gets the grace; one
/// that ignores it does not get to outlive its deadline. The sentence says both
/// what happened and how long it was given, so an operator reading a transcript
/// can tell a slow tool from a stuck one.
///
/// **The grace is the child's, and only the child's.** The wait polls the tool
/// this box was asked to run; a helper of its that ignores the ask cannot extend
/// a deadline the invocation has already overrun. The insist is the group's
/// either way — a leader with good manners is not a reason to leave its
/// stragglers running, which is the whole gap this closes. The pipes are pumped
/// across the grace for the same reason they are pumped across the wait: a tool
/// flushing a file on its way out has somewhere to write.
///
/// The final wait is unbounded, and what bounds it is the invariant that the
/// child is a member of the group just killed. That is the spawn boundary's to
/// hold, not this function's to re-check: a `Child::kill` here would be a second
/// mechanism for a case the boundary excludes, and one whose effect no test
/// could ever reach.