Skip to content

Commit f931959

Browse files
committed
test(supervisor): cover child registration reaper race
Signed-off-by: Evan Lezar <elezar@nvidia.com>
1 parent bd2bbe6 commit f931959

1 file changed

Lines changed: 47 additions & 0 deletions

File tree

‎crates/openshell-supervisor-process/src/managed_children.rs‎

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ mod tests {
7878
use nix::sys::wait::{Id, WaitPidFlag, WaitStatus, waitid, waitpid};
7979
use nix::unistd::Pid;
8080
use std::process::Command;
81+
use std::sync::mpsc;
82+
use std::time::Duration;
8183

8284
#[test]
8385
fn spawned_child_is_registered_before_returning() {
@@ -94,6 +96,51 @@ mod tests {
9496
unregister(pid);
9597
}
9698

99+
#[test]
100+
fn reaper_cannot_steal_child_while_registration_is_in_progress() {
101+
// Keep `spawn` paused after its logical child exists but before it
102+
// returns the PID. This is the exact window that previously let the
103+
// orphan reaper consume a fast child's status.
104+
let pid = 1_000_002_u32;
105+
let (spawn_entered_tx, spawn_entered_rx) = mpsc::channel();
106+
let (complete_spawn_tx, complete_spawn_rx) = mpsc::channel();
107+
let (reap_attempted_tx, reap_attempted_rx) = mpsc::channel();
108+
let (reap_result_tx, reap_result_rx) = mpsc::channel();
109+
110+
let spawn = std::thread::spawn(move || {
111+
spawn_registered(
112+
|| {
113+
spawn_entered_tx.send(()).unwrap();
114+
complete_spawn_rx.recv().unwrap();
115+
Ok::<_, ()>(pid)
116+
},
117+
|child| Some(*child),
118+
)
119+
.unwrap();
120+
});
121+
spawn_entered_rx.recv().unwrap();
122+
123+
let reaper = std::thread::spawn(move || {
124+
reap_attempted_tx.send(()).unwrap();
125+
let result = reap_if_unmanaged(i32::try_from(pid).unwrap(), || "reaped");
126+
reap_result_tx.send(result).unwrap();
127+
});
128+
reap_attempted_rx.recv().unwrap();
129+
130+
assert!(
131+
reap_result_rx
132+
.recv_timeout(Duration::from_millis(50))
133+
.is_err(),
134+
"the reaper must wait until the child is registered"
135+
);
136+
137+
complete_spawn_tx.send(()).unwrap();
138+
spawn.join().unwrap();
139+
assert_eq!(reap_result_rx.recv().unwrap(), None);
140+
reaper.join().unwrap();
141+
unregister(pid);
142+
}
143+
97144
#[test]
98145
fn unmanaged_child_can_be_reaped() {
99146
let result = reap_if_unmanaged(1_000_001, || "reaped");

0 commit comments

Comments
 (0)