@@ -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