@@ -22,7 +22,7 @@ use syscall_handler::SyscallHandler;
2222use tokio:: task:: spawn_blocking;
2323
2424use crate :: {
25- Command , TrackedChild ,
25+ ChildTermination , Command , TrackedChild ,
2626 arena:: PathAccessArena ,
2727 error:: SpawnError ,
2828 ipc:: { OwnedReceiverLockGuard , SHM_CAPACITY } ,
@@ -131,26 +131,33 @@ pub(crate) async fn spawn_impl(mut command: Command) -> Result<TrackedChild, Spa
131131 // tokio_command.spawn blocks while executing the `pre_exec` closure.
132132 // Run it inside spawn_blocking to avoid blocking the tokio runtime, especially the supervisor loop,
133133 // which needs to accept incoming connections while `pre_exec` is connecting to it.
134- let child = spawn_blocking ( move || tokio_command. spawn ( ) )
134+ let mut child = spawn_blocking ( move || tokio_command. spawn ( ) )
135135 . await
136136 . map_err ( |err| SpawnError :: OsSpawnError ( err. into ( ) ) ) ?
137137 . map_err ( SpawnError :: OsSpawnError ) ?;
138138
139- let arenas_future = async move {
140- let arenas = std:: iter:: once ( exec_resolve_accesses) ;
141- #[ cfg( target_os = "linux" ) ]
142- let arenas =
143- arenas. chain ( supervisor. stop ( ) . await ?. into_iter ( ) . map ( |handler| handler. into_arena ( ) ) ) ;
144- io:: Result :: Ok ( arenas. collect :: < Vec < _ > > ( ) )
145- } ;
146-
147- let accesses_future = async move {
148- let arenas = arenas_future. await ?;
149- // `receiver.lock()` blocks. Run it inside `spawn_blocking` to avoid blocking the tokio runtime.
150- let ipc_receiver_lock_guard = OwnedReceiverLockGuard :: lock_async ( ipc_receiver) . await ?;
151- Ok ( PathAccessIterable { arenas, ipc_receiver_lock_guard } )
152- }
153- . boxed ( ) ;
154-
155- Ok ( TrackedChild { tokio_child : child, accesses_future } )
139+ Ok ( TrackedChild {
140+ stdin : child. stdin . take ( ) ,
141+ stdout : child. stdout . take ( ) ,
142+ stderr : child. stderr . take ( ) ,
143+ wait_handle : tokio:: spawn ( async move {
144+ let status = child. wait ( ) . await ?;
145+
146+ let arenas = std:: iter:: once ( exec_resolve_accesses) ;
147+ // Stop the supervisor and collect path accesses from it.
148+ #[ cfg( target_os = "linux" ) ]
149+ let arenas = arenas
150+ . chain ( supervisor. stop ( ) . await ?. into_iter ( ) . map ( |handler| handler. into_arena ( ) ) ) ;
151+ let arenas = arenas. collect :: < Vec < _ > > ( ) ;
152+
153+ // Lock the ipc channel after the child has exited.
154+ // We are not interested in path accesses from decendants after the main child has exited.
155+ let ipc_receiver_lock_guard = OwnedReceiverLockGuard :: lock_async ( ipc_receiver) . await ?;
156+ let path_accesses = PathAccessIterable { arenas, ipc_receiver_lock_guard } ;
157+
158+ io:: Result :: Ok ( ChildTermination { status, path_accesses } )
159+ } )
160+ . map ( |f| io:: Result :: Ok ( f??) ) // flatten JoinError and io::Result
161+ . boxed ( ) ,
162+ } )
156163}
0 commit comments