diff --git a/src/main.rs b/src/main.rs index 28f9432..7943075 100644 --- a/src/main.rs +++ b/src/main.rs @@ -95,15 +95,10 @@ fn main() { } else { child.terminate(); } - - child.wait(); }, - Signal::Stop => { - child.pause(); - }, - Signal::Continue => { - child.resume(); - } + Signal::Stop => child.pause(), + Signal::Continue => child.resume(), + Signal::ChildExit => child.reap(), } } } @@ -153,6 +148,7 @@ fn main() { } loop { + debug!("Waiting for filesystem activity"); let paths = wait(&rx, &filter); if let Some(path) = paths.get(0) { debug!("Path updated: {:?}", path); @@ -182,6 +178,7 @@ fn main() { cli::clear_screen(); } + debug!("Launching child process"); { let mut guard = child_process.write().unwrap(); *guard = Some(process::spawn(&args.cmd, paths)); diff --git a/src/process.rs b/src/process.rs index a147e66..6d98061 100644 --- a/src/process.rs +++ b/src/process.rs @@ -12,9 +12,12 @@ mod imp { use std::io::Result; use std::path::PathBuf; use std::process::Command; + use std::sync::*; pub struct Process { pgid: pid_t, + lock: Mutex, + cvar: Condvar, } impl Process { @@ -55,7 +58,11 @@ mod imp { } let _ = close(w); - Ok(Process { pgid: child }) + Ok(Process { + pgid: child, + lock: Mutex::new(false), + cvar: Condvar::new(), + }) } Ok(ForkResult::Child) => { let _ = setpgid(0, 0); @@ -85,6 +92,25 @@ mod imp { self.signal(SIGTSTP); } + pub fn reap(&self) { + use nix::sys::wait::*; + + let mut finished = true; + loop { + match waitpid(-self.pgid, Some(WNOHANG)) { + Ok(WaitStatus::Exited(_, _)) => finished = finished && true, + Ok(WaitStatus::Signaled(_, _, _)) => finished = finished && true, + Ok(_) => finished = false, + Err(_) => break + } + } + + if finished { + let mut done = self.lock.lock().unwrap(); + *done = true; + self.cvar.notify_one(); + } + } pub fn resume(&self) { self.signal(SIGCONT); @@ -106,9 +132,10 @@ mod imp { } pub fn wait(&self) { - use nix::sys::wait::waitpid; - - while let Ok(_) = waitpid(-self.pgid, None) {} + let mut done = self.lock.lock().unwrap(); + while !*done { + done = self.cvar.wait(done).unwrap(); + } } } } @@ -181,6 +208,9 @@ mod imp { pub fn pause(&self) { } + pub fn reap(&self) { + } + pub fn resume(&self) { } @@ -190,7 +220,6 @@ mod imp { } } - pub fn wait(&self) { unsafe { let _ = WaitForSingleObject(self.job, INFINITE); diff --git a/src/signal.rs b/src/signal.rs index 5a8dd47..9161326 100644 --- a/src/signal.rs +++ b/src/signal.rs @@ -7,7 +7,8 @@ lazy_static! { pub enum Signal { Terminate, Stop, - Continue + Continue, + ChildExit } #[cfg(unix)] @@ -15,6 +16,7 @@ pub fn install_handler(handler: F) where F: Fn(self::Signal) + 'static + Send + Sync { use std::thread; + use libc::c_int; use nix::sys::signal::*; // Mask all signals interesting to us. The mask propagates @@ -24,20 +26,32 @@ pub fn install_handler(handler: F) mask.add(SIGINT); mask.add(SIGTSTP); mask.add(SIGCONT); + mask.add(SIGCHLD); mask.thread_set_mask().expect("unable to set signal mask"); set_handler(handler); + // Indicate interest in SIGCHLD by setting a dummy handler + pub extern "C" fn sigchld_handler(_: c_int) { + } + + unsafe { + let _ = sigaction(SIGCHLD, &SigAction::new( + SigHandler::Handler(sigchld_handler), SaFlags::empty(), SigSet::empty())); + } + // Spawn a thread to catch these signals thread::spawn(move || { loop { let raw_signal = mask.wait().expect("unable to sigwait"); + debug!("Received {:?}", raw_signal); let sig = match raw_signal { SIGTERM => self::Signal::Terminate, SIGINT => self::Signal::Terminate, SIGTSTP => self::Signal::Stop, SIGCONT => self::Signal::Continue, + SIGCHLD => self::Signal::ChildExit, _ => unreachable!() }; @@ -45,10 +59,12 @@ pub fn install_handler(handler: F) invoke(sig); // Restore default behavior for received signal and unmask it - let default_action = SigAction::new(SigHandler::SigDfl, SaFlags::empty(), SigSet::empty()); + if raw_signal != SIGCHLD { + let default_action = SigAction::new(SigHandler::SigDfl, SaFlags::empty(), SigSet::empty()); - unsafe { - let _ = sigaction(raw_signal, &default_action); + unsafe { + let _ = sigaction(raw_signal, &default_action); + } } let mut new_mask = SigSet::empty();