driver-manager: reaper captures waitpid status + watchdog exit on panic (N18 Q2+Sigterm reset + Bug5)
Two changes in one file: - Q2 mechanical: change the spawn_reaper_thread closure signature from `Fn(u32)` to `Fn(u32, i32)`; capture `waitpid` status via `&mut status`; pass both `res` and `status` to the reaper callback. The existing `reaper_thread_fires_on_flag` test's closure is updated to `|pid: u32, _status: i32|`. - Bug5: `run_watchdog` no longer silently `return;`s on a reaper thread panic. A `TEST_NO_EXIT` cfg-conditional const gates the production path (`std::process::exit(1)` so init respawns driver-manager) vs the test path (log-and-return so the existing watchdog tests are not affected). The remaining pieces of Q2 (status routing in the reaper callback) live in the main.rs Q1+Q2+S3 commit.
This commit is contained in:
@@ -21,6 +21,11 @@ use std::time::Duration;
|
||||
|
||||
static REAP_FLAG: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
#[cfg(test)]
|
||||
const TEST_NO_EXIT: bool = true;
|
||||
#[cfg(not(test))]
|
||||
const TEST_NO_EXIT: bool = false;
|
||||
|
||||
extern "C" fn sigchld_handler(_sig: i32) {
|
||||
REAP_FLAG.store(true, Ordering::SeqCst);
|
||||
}
|
||||
@@ -38,7 +43,7 @@ pub fn set_reap_flag() {
|
||||
/// `spawned` map).
|
||||
pub fn spawn_reaper_thread<F>(on_reap: F) -> thread::JoinHandle<()>
|
||||
where
|
||||
F: Fn(u32) + Send + 'static,
|
||||
F: Fn(u32, i32) + Send + 'static,
|
||||
{
|
||||
unsafe {
|
||||
libc::signal(libc::SIGCHLD, sigchld_handler as *const () as libc::sighandler_t);
|
||||
@@ -93,6 +98,12 @@ fn run_watchdog(handle: thread::JoinHandle<()>) {
|
||||
);
|
||||
}
|
||||
}
|
||||
if !TEST_NO_EXIT {
|
||||
log::error!("driver-manager-sigchld: reaper unrecoverable; exiting driver-manager for init respawn");
|
||||
std::process::exit(1);
|
||||
} else {
|
||||
log::warn!("driver-manager-sigchld: reaper exited (test mode, no process exit)");
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -118,7 +129,7 @@ fn panic_payload_to_string(payload: &Box<dyn std::any::Any + Send>) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
fn run<F: Fn(u32)>(on_reap: F) {
|
||||
fn run<F: Fn(u32, i32)>(on_reap: F) {
|
||||
log::info!("sigchld-reaper: worker started");
|
||||
loop {
|
||||
std::thread::sleep(Duration::from_millis(100));
|
||||
@@ -130,12 +141,13 @@ fn run<F: Fn(u32)>(on_reap: F) {
|
||||
// for a child so the call will not return success for a
|
||||
// non-zombie pid. We loop until ECHILD.
|
||||
loop {
|
||||
let res = unsafe { libc::waitpid(-1, std::ptr::null_mut(), libc::WNOHANG) };
|
||||
let mut status: i32 = 0;
|
||||
let res = unsafe { libc::waitpid(-1, &mut status, libc::WNOHANG) };
|
||||
if res <= 0 {
|
||||
break;
|
||||
}
|
||||
log::info!("sigchld-reaper: reaped pid {}", res);
|
||||
on_reap(res as u32);
|
||||
log::info!("sigchld-reaper: reaped pid {} status 0x{:x}", res, status as u32);
|
||||
on_reap(res as u32, status);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -158,7 +170,7 @@ mod tests {
|
||||
fn reaper_thread_fires_on_flag() {
|
||||
let seen: std::sync::Arc<AtomicU32> = std::sync::Arc::new(AtomicU32::new(0));
|
||||
let s = seen.clone();
|
||||
let handle = spawn_reaper_thread(move |pid| {
|
||||
let handle = spawn_reaper_thread(move |pid: u32, _status: i32| {
|
||||
// The worker reads the flag, calls waitpid, and invokes
|
||||
// this on every reaped pid. In the test environment we
|
||||
// cannot have an actual child to reap, so the loop will
|
||||
|
||||
Reference in New Issue
Block a user