@@ -7,7 +7,7 @@ use std::sync::Arc;
77use std:: time:: { Duration , Instant } ;
88use tracing:: { error, info, instrument, warn} ;
99
10- use crate :: { instance:: Instance , network:: NetworkAllocation } ;
10+ use crate :: { instance:: Instance , network:: NetworkAllocation , shutdown } ;
1111
1212/// Run the per-slot loop for a given role and slot index.
1313/// This function is intended to run in its own OS process or thread.
@@ -42,6 +42,11 @@ pub fn run_slot(config: Arc<ManagerConfig>, role: &Role, idx: usize) -> Result<(
4242 let mut cycle_n: u64 = 0 ;
4343
4444 loop {
45+ if shutdown:: requested ( ) {
46+ info ! ( role = %role. name, idx, "shutdown requested, exiting boot loop" ) ;
47+ break ;
48+ }
49+
4550 let pid_file = instance. pid_file_path ( ) ;
4651
4752 // Graceful restart: if Firecracker is already running for this slot, reattach
@@ -53,7 +58,10 @@ pub fn run_slot(config: Arc<ManagerConfig>, role: &Role, idx: usize) -> Result<(
5358 pid,
5459 "Firecracker already running, reattaching"
5560 ) ;
61+ // Register so the shutdown watcher can stop the reattached VM too.
62+ shutdown:: register_child ( & role. slug ( ) , idx, pid) ;
5663 wait_for_pid ( pid) ;
64+ shutdown:: unregister_child ( & role. slug ( ) , idx) ;
5765 remove_pid_file ( & pid_file) ;
5866 // Fall through to next cycle after the job finishes
5967 continue ;
@@ -71,6 +79,7 @@ pub fn run_slot(config: Arc<ManagerConfig>, role: &Role, idx: usize) -> Result<(
7179 match instance. start ( ) {
7280 Ok ( mut child) => {
7381 let pid = child. id ( ) ;
82+ shutdown:: register_child ( & role. slug ( ) , idx, pid) ;
7483 if let Err ( e) = write_pid_file ( & pid_file, pid) {
7584 error ! ( role = %role. name, idx, pid, error = %e, "failed to write PID file" ) ;
7685 }
@@ -154,11 +163,18 @@ pub fn run_slot(config: Arc<ManagerConfig>, role: &Role, idx: usize) -> Result<(
154163 }
155164 }
156165
166+ shutdown:: unregister_child ( & role. slug ( ) , idx) ;
157167 remove_pid_file ( & pid_file) ;
158168 }
159169 Err ( e) => {
160170 error ! ( role = %role. name, idx, cycle = cycle_n, error = %e, "boot cycle failed" ) ;
161- std:: thread:: sleep ( Duration :: from_secs ( 20 ) ) ;
171+ // Interruptible retry back-off so shutdown isn't delayed by up to 20s.
172+ for _ in 0 ..20 {
173+ if shutdown:: requested ( ) {
174+ break ;
175+ }
176+ std:: thread:: sleep ( Duration :: from_secs ( 1 ) ) ;
177+ }
162178 instance. reset ( ) ;
163179 continue ;
164180 }
@@ -169,6 +185,12 @@ pub fn run_slot(config: Arc<ManagerConfig>, role: &Role, idx: usize) -> Result<(
169185 error ! ( role = %role. name, idx, error = %e, "cache clear failed" ) ;
170186 }
171187 }
188+
189+ // Shutdown path: make sure nothing from the last cycle is left behind that
190+ // would wedge the slot on the next manager start.
191+ instance. cleanup_stale_mount ( ) ;
192+ info ! ( role = %role. name, idx, "slot shut down cleanly" ) ;
193+ Ok ( ( ) )
172194}
173195
174196fn read_child_stderr ( child : & mut std:: process:: Child ) -> String {
0 commit comments