@@ -387,46 +387,71 @@ fn handle(rx: Receiver<ModuleMessage>, state: Arc<Mutex<ManagerState>>) {
387387 } else {
388388 error ! ( "Module {name} exited with error status" ) ;
389389 thread:: spawn ( move || {
390- thread:: sleep ( Duration :: from_secs ( 1 ) ) ;
391- let state = & mut state_clone
392- . lock ( )
393- . expect ( "Failed to acquire manager_state lock" ) ;
394- let restart_count =
395- state. modules_restart_count . get ( & name_clone) . unwrap_or ( & 0 ) ;
396-
397- let pending_shutdown = state
398- . modules_pending_shutdown
399- . get ( & name_clone)
400- . unwrap_or ( & false ) ;
401-
402- if * pending_shutdown {
403- return ;
404- }
405- if * restart_count < 3 {
406- let new_count = * restart_count + 1 ;
407- state
408- . modules_restart_count
409- . insert ( name_clone. clone ( ) , new_count) ;
410- // Get the stored arguments for this module
411- let stored_args =
412- state. modules_args . get ( & name_clone) . cloned ( ) . flatten ( ) ;
413- state. start_module ( & name_clone, stored_args. as_ref ( ) ) ;
414- let app = & * get_app_handle ( ) . lock ( ) . expect ( "Failed to get app handle" ) ;
390+ let ( should_restart, restart_info) = {
391+ let state = & mut state_clone
392+ . lock ( )
393+ . expect ( "Failed to acquire manager_state lock" ) ;
394+ let restart_count =
395+ state. modules_restart_count . get ( & name_clone) . unwrap_or ( & 0 ) ;
396+
397+ let pending_shutdown = state
398+ . modules_pending_shutdown
399+ . get ( & name_clone)
400+ . unwrap_or ( & false ) ;
415401
416- app. dialog ( )
417- . message ( format ! ( "{name_clone} crashed. Restarting..." ) )
418- . kind ( MessageDialogKind :: Warning )
419- . title ( "Warning" )
420- . show ( |_| { } ) ;
421- error ! ( "Module {name_clone} crashed and is being restarted" ) ;
402+ // If shutdown is pending, exit early
403+ if * pending_shutdown {
404+ return ; // Exit the entire thread
405+ }
406+
407+ if * restart_count < 3 {
408+ // Exponential backoff: 2^(restart_count + 1) seconds
409+ // restart_count 0 -> 2 seconds, 1 -> 4 seconds, 2 -> 8 seconds
410+ let delay_secs = 2u64 . pow ( * restart_count + 1 ) ;
411+ info ! ( "Module {name_clone} will restart in {delay_secs} seconds (attempt {} of 3)" , * restart_count + 1 ) ;
412+ ( true , Some ( ( delay_secs, * restart_count) ) )
413+ } else {
414+ ( false , None )
415+ }
416+ // state is automatically dropped here when the block ends
417+ } ;
418+
419+ if should_restart {
420+ if let Some ( ( secs, restart_count) ) = restart_info {
421+ // Show dialog BEFORE sleeping
422+ let app =
423+ & * get_app_handle ( ) . lock ( ) . expect ( "Failed to get app handle" ) ;
424+ app. dialog ( )
425+ . message ( format ! ( "{name_clone} crashed. Restarting..." ) )
426+ . kind ( MessageDialogKind :: Warning )
427+ . title ( "Warning" )
428+ . show ( |_| { } ) ;
429+ error ! ( "Module {name_clone} crashed and will be restarted" ) ;
430+
431+ thread:: sleep ( Duration :: from_secs ( secs) ) ;
432+
433+ let state = & mut state_clone
434+ . lock ( )
435+ . expect ( "Failed to acquire manager_state lock" ) ;
436+
437+ state
438+ . modules_restart_count
439+ . insert ( name_clone. clone ( ) , restart_count + 1 ) ;
440+ // Get the stored arguments for this module
441+ let stored_args =
442+ state. modules_args . get ( & name_clone) . cloned ( ) . flatten ( ) ;
443+ state. start_module ( & name_clone, stored_args. as_ref ( ) ) ;
444+ }
422445 } else {
423- // Prevent further restarts
446+ // Restart limit reached
447+ let state = & mut state_clone
448+ . lock ( )
449+ . expect ( "Failed to acquire manager_state lock" ) ;
424450 state
425451 . modules_pending_shutdown
426452 . insert ( name_clone. clone ( ) , true ) ;
427453
428454 let app = & * get_app_handle ( ) . lock ( ) . expect ( "Failed to get app handle" ) ;
429-
430455 app. dialog ( )
431456 . message ( format ! (
432457 "{name_clone} keeps on crashing. Restart limit reached."
0 commit comments