nextest_runner/
double_spawn.rs1use std::path::Path;
31
32#[derive(Clone, Debug)]
37pub struct DoubleSpawnInfo {
38 inner: imp::DoubleSpawnInfo,
39}
40
41impl DoubleSpawnInfo {
42 pub const SUBCOMMAND_NAME: &'static str = "__double-spawn";
44
45 pub fn try_enable() -> Self {
49 Self {
50 inner: imp::DoubleSpawnInfo::try_enable(),
51 }
52 }
53
54 pub fn disabled() -> Self {
56 Self {
57 inner: imp::DoubleSpawnInfo::disabled(),
58 }
59 }
60
61 pub fn current_exe(&self) -> Option<&Path> {
65 self.inner.current_exe()
66 }
67
68 pub fn spawn_context(&self) -> Option<DoubleSpawnContext> {
70 self.current_exe().map(|_| DoubleSpawnContext::new())
71 }
72}
73
74#[derive(Debug)]
78pub struct DoubleSpawnContext {
79 #[expect(dead_code)]
81 inner: imp::DoubleSpawnContext,
82}
83
84impl DoubleSpawnContext {
85 #[inline]
86 fn new() -> Self {
87 Self {
88 inner: imp::DoubleSpawnContext::new(),
89 }
90 }
91
92 pub fn finish(self) {}
94}
95
96pub fn double_spawn_child_init() {
98 imp::double_spawn_child_init()
99}
100
101#[cfg(unix)]
102mod imp {
103 use super::*;
104 use nix::sys::signal::{SigSet, Signal};
105 use std::path::PathBuf;
106 use tracing::warn;
107
108 #[derive(Clone, Debug)]
109 pub(super) struct DoubleSpawnInfo {
110 current_exe: Option<PathBuf>,
111 }
112
113 impl DoubleSpawnInfo {
114 #[inline]
115 pub(super) fn try_enable() -> Self {
116 let current_exe = get_current_exe().map_or_else(
120 |error| {
121 warn!(
122 "unable to determine current exe, will not use double-spawning \
123 for better isolation: {error}"
124 );
125 None
126 },
127 Some,
128 );
129 Self { current_exe }
130 }
131
132 #[inline]
133 pub(super) fn disabled() -> Self {
134 Self { current_exe: None }
135 }
136
137 #[inline]
138 pub(super) fn current_exe(&self) -> Option<&Path> {
139 self.current_exe.as_deref()
140 }
141 }
142
143 #[cfg(target_os = "linux")]
144 fn get_current_exe() -> std::io::Result<PathBuf> {
145 static PROC_SELF_EXE: &str = "/proc/self/exe";
146
147 let path = Path::new(PROC_SELF_EXE);
150 match path.symlink_metadata() {
151 Ok(_) => Ok(path.to_owned()),
152 Err(e) if e.kind() == std::io::ErrorKind::NotFound => Err(std::io::Error::other(
153 "no /proc/self/exe available. Is /proc mounted?",
154 )),
155 Err(e) => Err(e),
156 }
157 }
158
159 #[cfg(not(target_os = "linux"))]
162 #[inline]
163 fn get_current_exe() -> std::io::Result<PathBuf> {
164 std::env::current_exe()
165 }
166
167 #[derive(Debug)]
168 pub(super) struct DoubleSpawnContext {
169 to_unblock: Option<SigSet>,
170 }
171
172 impl DoubleSpawnContext {
173 #[inline]
174 pub(super) fn new() -> Self {
175 let mut sigset = SigSet::empty();
178 sigset.add(Signal::SIGTSTP);
179 let to_unblock = sigset.thread_block().ok().map(|()| sigset);
180 Self { to_unblock }
181 }
182 }
183
184 impl Drop for DoubleSpawnContext {
185 fn drop(&mut self) {
186 if let Some(sigset) = &self.to_unblock {
187 _ = sigset.thread_unblock();
188 }
189 }
190 }
191
192 #[inline]
193 pub(super) fn double_spawn_child_init() {
194 let mut sigset = SigSet::empty();
195 sigset.add(Signal::SIGTSTP);
196 if sigset.thread_unblock().is_err() {
197 warn!("[double-spawn] unable to unblock SIGTSTP in child");
198 }
199 }
200}
201
202#[cfg(not(unix))]
203mod imp {
204 use super::*;
205
206 #[derive(Clone, Debug)]
207 pub(super) struct DoubleSpawnInfo {}
208
209 impl DoubleSpawnInfo {
210 #[inline]
211 pub(super) fn try_enable() -> Self {
212 Self {}
213 }
214
215 #[inline]
216 pub(super) fn disabled() -> Self {
217 Self {}
218 }
219
220 #[inline]
221 pub(super) fn current_exe(&self) -> Option<&Path> {
222 None
223 }
224 }
225
226 #[derive(Debug)]
227 pub(super) struct DoubleSpawnContext {}
228
229 impl DoubleSpawnContext {
230 #[inline]
231 pub(super) fn new() -> Self {
232 Self {}
233 }
234 }
235
236 #[inline]
237 pub(super) fn double_spawn_child_init() {}
238}