@@ -49,15 +49,18 @@ pub mod shutdown;
4949pub use shutdown:: { ShutdownCoordinator , ShutdownHandle , ShutdownSignal } ;
5050
5151use crate :: lifecycle:: LifecycleHandler ;
52- use crate :: pipes:: { completion_pipes, create_paired_pipes, FdStringExt , PipeMode } ;
52+ use crate :: pipes:: {
53+ completion_pipes, create_paired_pipes, CompletionReceiver , CompletionSender , FdStringExt ,
54+ PipeMode ,
55+ } ;
5356use crate :: restart_coordination_socket:: {
5457 RestartCoordinationSocket , RestartMessage , RestartRequest , RestartResponse ,
5558} ;
5659use anyhow:: anyhow;
5760use futures:: stream:: { Stream , StreamExt } ;
5861use std:: env;
5962use std:: ffi:: OsString ;
60- use std:: fs:: remove_file;
63+ use std:: fs:: { remove_file, File as StdFile } ;
6164use std:: future:: Future ;
6265use std:: io;
6366use std:: os:: fd:: { AsFd , AsRawFd , BorrowedFd , OwnedFd , RawFd } ;
@@ -92,6 +95,8 @@ pub struct RestartConfig {
9295 pub environment : Vec < ( OsString , OsString ) > ,
9396 /// Receive fine-grained events on the lifecycle of the new process and support data transfer.
9497 pub lifecycle_handler : Box < dyn LifecycleHandler > ,
98+ /// Exits early when child process fail to start
99+ pub exit_on_error : bool ,
95100}
96101
97102impl RestartConfig {
@@ -133,6 +138,7 @@ impl Default for RestartConfig {
133138 coordination_socket_path : Default :: default ( ) ,
134139 environment : vec ! [ ] ,
135140 lifecycle_handler : Box :: new ( lifecycle:: NullLifecycleHandler ) ,
141+ exit_on_error : true ,
136142 }
137143 }
138144}
@@ -235,7 +241,11 @@ pub fn spawn_restart_task(
235241 return Ok ( child) ;
236242 }
237243 Err ( ChildSpawnError :: ChildError ( e) ) => {
238- log:: error!( "Restart failed: {}" , e) ;
244+ if settings. exit_on_error {
245+ return Err ( anyhow ! ( "Restart failed: {}" , e) ) ;
246+ } else {
247+ log:: error!( "Restart failed: {}" , e) ;
248+ }
239249 }
240250 Err ( ChildSpawnError :: RestartThreadGone ) => {
241251 res?;
@@ -406,7 +416,7 @@ async fn spawn_child(
406416 let process_name = args. next ( ) . unwrap ( ) ;
407417
408418 // Create a pipe for the child to notify us on successful startup
409- let ( mut notif_r, notif_w) = completion_pipes ( ) ?;
419+ let ( notif_r, notif_w) = completion_pipes ( ) ?;
410420
411421 // And another pair of pipes to hand over data to the child process.
412422 let ( handover_r, handover_w) = create_paired_pipes ( PipeMode :: ParentWrites ) ?;
@@ -429,16 +439,34 @@ async fn spawn_child(
429439 } ) ;
430440 }
431441 }
432- let child = cmd. spawn ( ) ?;
442+ let mut child = cmd. spawn ( ) ?;
443+
444+ if let Err ( e) = send_parent_state ( lifecycle_handler, notif_r, notif_w, handover_w) . await {
445+ if child. kill ( ) . is_err ( ) {
446+ log:: error!( "Child process has already exited. Failed to send parent state: {e:?}" ) ;
447+ } else {
448+ log:: error!( "Killed child process because failed to send parent state: {e:?}" ) ;
449+ }
450+ return Err ( e) ;
451+ }
452+
453+ Ok ( child)
454+ }
433455
456+ async fn send_parent_state (
457+ lifecycle_handler : & mut dyn LifecycleHandler ,
458+ mut notif_r : CompletionReceiver ,
459+ notif_w : CompletionSender ,
460+ handover_w : StdFile ,
461+ ) -> io:: Result < ( ) > {
434462 lifecycle_handler
435463 . send_to_new_process ( Box :: pin ( File :: from ( handover_w) ) )
436464 . await ?;
437465
438466 // only the child needs the write end
439467 drop ( notif_w) ;
440468 match notif_r. recv ( ) {
441- Ok ( _) => Ok ( child ) ,
469+ Ok ( _) => Ok ( ( ) ) ,
442470 Err ( e) => {
443471 lifecycle_handler. new_process_failed ( ) . await ;
444472 Err ( e)
0 commit comments