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
//! Child-process plumbing for the process adapter: spawn, drain, frame, reap.
//! 进程适配器的子进程底层机制:派生、排空、分帧、回收。
//!
//! This is the half of `process` that talks to the operating system. It is
//! separate so the adapter's contract — limits, the program, the operation
//! framing the host promises — stays readable in one page, and so the pipe and
//! spawn hazards below get room for the explanation they need.
//! 这是 `process` 中与操作系统对话的那一半。它独立出来,是为了让适配器的契约——限制、
//! 程序、宿主承诺的操作分帧——保持在一页内可读,也让下面的管道与派生陷阱有地方把话说明白。
use ;
use crateHostError;
use ;
/// How often the child's exit status is polled while it runs.
/// 子进程运行期间轮询其退出状态的间隔。
pub const POLL_INTERVAL: Duration = from_millis;
/// Attempts made when `exec` reports the staged file as busy.
/// `exec` 报告暂存文件忙时的尝试次数。
const SPAWN_ATTEMPTS: usize = 10;
/// Spawn the staged plugin, retrying the one transient failure `exec` reports.
/// 派生已暂存的插件,并对 `exec` 唯一会报的瞬时失败进行重试。
///
/// `ETXTBSY` ("Text file busy") means the file is open for writing somewhere.
/// The staged copy is private and was just written by this host, so the
/// condition is a race with a concurrent fork/exec rather than a real conflict:
/// running several plugin calls at once hit it here, reported as
/// `ExecutableFileBusy` by `stderr_larger_than_the_pipe_buffer_does_not_block`.
/// A host that spawns plugins from several threads can hit the same race, so it
/// is retried a bounded number of times and then reported as the I/O error it
/// is; nothing else is retried.
/// `ETXTBSY`("Text file busy")表示该文件在某处被以可写方式打开。这份暂存副本是私有的、
/// 刚由本宿主写入,因此该状况是与并发的 fork/exec 竞争,而不是真实冲突:并发执行多个插件
/// 调用时在这里撞上了它,被 `stderr_larger_than_the_pipe_buffer_does_not_block` 报成
/// `ExecutableFileBusy`。从多个线程派生插件的宿主会撞上同一种竞争,因此这里做有界重试,
/// 之后仍作为 I/O 错误上报;其他错误一律不重试。
///
/// The child's environment and working directory are the host's choice, not the
/// child's: `inherit_env: false` clears what the host would otherwise hand over,
/// the program's own variables are then the only ones present, and a configured
/// `current_dir` decides where it runs. None of that confines the filesystem or
/// the network — that still has to come from outside this workspace.
/// 子进程的环境与工作目录由宿主选择,而不是由子进程决定:`inherit_env: false` 清掉宿主本来
/// 会交出去的东西,此后程序自己声明的变量是唯一存在的;配置了 `current_dir` 就由它决定在哪里
/// 运行。这些都不约束文件系统或网络——那仍然必须来自本工作区之外。
pub
/// Bytes of stderr kept for the failure message.
/// 失败消息保留的 stderr 字节数。
const STDERR_BYTES: usize = 4096;
/// Read a child's stderr to EOF, keeping at most [`STDERR_BYTES`].
/// 读到子进程 stderr 的 EOF,最多保留 [`STDERR_BYTES`] 字节。
///
/// Reading to EOF even after the cap is reached is deliberate: stopping early
/// would let a chatty child block on a full pipe, which is the same failure this
/// adapter removed from stdout.
/// 达到上限后仍读到 EOF 是有意的:提前停止会让话多的子进程阻塞在满管道上,那正是本适配器
/// 从 stdout 上移除掉的同一种故障。
pub
/// Read a stream to EOF, keeping nothing.
/// 把一个流读到 EOF,不保留任何内容。
///
/// Used after a response frame: a child that keeps writing after its answer must
/// not block on a full pipe. If it does, the host waits for an exit that cannot
/// come, kills the child at the deadline and reports a timeout — discarding the
/// answer it already holds.
/// 用在响应帧之后:已经给出答案却继续写入的子进程绝不能阻塞在满管道上。一旦阻塞,宿主会去
/// 等一个不可能到来的退出,在超时点杀掉子进程并报出超时——同时丢掉它其实已经拿到的答案。
pub
/// Kill the child and reap it, even when the kill itself fails.
/// 杀掉子进程并回收它,即使 kill 本身失败。
///
/// `Child::kill` fails with `InvalidInput` when the child has already been
/// reaped; propagating that with `?` would skip `Child::wait` and leave a
/// zombie, so both steps are attempted and neither is reported.
/// 子进程已被回收时 `Child::kill` 会以 `InvalidInput` 失败;用 `?` 传播它会跳过
/// `Child::wait` 并留下僵尸进程,因此两步都尝试、都不上报。
pub
/// Read one response frame: the declared length, then that many bytes.
/// 读取一个响应帧:先是声明的长度,然后是相应字节数。
///
/// The declared length is checked against the cap *before* the buffer is
/// allocated, so an over-limit declaration costs nothing.
/// 声明的长度在分配缓冲区**之前**就与上限比较,因此超限的声明不花任何代价。
pub