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
//! Bounded ETXTBSY retry wrapper for spawning the `trusty-embedderd` sidecar.
//!
//! Why: isolated in a sibling file (rather than inline in `supervisor.rs`) to
//! keep `supervisor.rs` under its 500-SLOC production-file cap while still
//! sharing this crate's usual per-module split (`error.rs`, `stdio.rs`, …)
//! rather than a test-only `#[path]` trick.
//!
//! Test: exercised indirectly via every test in
//! `supervisor::tests::shutdown_tests` (all of which call
//! `EmbedderSupervisor::spawn_stdio` -> `spawn_child` -> `spawn_embedderd`
//! before exercising shutdown), especially
//! `supervisor_dropped_handle_does_not_busy_spin` (#3570).
use Path;
use Stdio;
use Duration;
use ;
use SupervisorConfig;
/// Build the `trusty-embedderd --stdio` command and spawn it with a bounded
/// ETXTBSY retry.
///
/// Why: extracted out of `supervisor::spawn_child` so command construction
/// and the retry live together — `Command` is not `Clone`, so retrying a
/// spawn means rebuilding it from scratch on every attempt anyway.
/// What: `Command::new(binary_path).arg("--stdio")` with piped stdin/stdout,
/// inherited stderr, `kill_on_drop(true)`, and (when
/// `config.sidecar_batch_size` is `Some(n)`) `TRUSTY_EMBED_BATCH_SIZE=n`
/// (issue #747 Fix C) — then delegates to `spawn_with_etxtbsy_retry`.
/// Test: `supervisor_dropped_handle_does_not_busy_spin`,
/// `supervisor_shutdown_kills_child`.
pub async
/// Spawn a freshly-built `Command`, retrying on ETXTBSY (os error 26).
///
/// Why: `execve` can fail with `ETXTBSY` ("Text file busy") when the kernel
/// still considers the target inode open-for-write — e.g. when a just-written,
/// just-chmod'd mock `trusty-embedderd` script is exec'd before the page-cache
/// flush settles. This is the same race class already diagnosed and fixed in
/// `trusty-agents` under #866, #1528, and #1634 (see
/// `spawn_with_etxtbsy_retry` in `claude_code_runner/mod.rs`); this transplants
/// that exact bounded-retry shape rather than inventing a new one, so the
/// regression test `supervisor_dropped_handle_does_not_busy_spin` (#3570) — and
/// the real sidecar spawn path — stop hitting the transient kernel state.
/// What: calls `build()` to obtain a fresh `Command` on each attempt (required
/// because `Command` is not `Clone`) and `.spawn()`s it. On `ETXTBSY` it backs
/// off with a short exponential delay (≤3 attempts, 5 ms base) and retries; any
/// other error, or the success path, returns immediately. The bound keeps real
/// failures (missing binary, permission denied) from being masked or delayed.
/// Test: `supervisor_dropped_handle_does_not_busy_spin`,
/// `supervisor_shutdown_kills_child`.
async