SIGCHLD handling

This commit is contained in:
Matt Green 2016-12-20 11:44:18 -05:00
parent a925cb356a
commit b2c809c87a
3 changed files with 59 additions and 17 deletions

View File

@ -95,15 +95,10 @@ fn main() {
} else { } else {
child.terminate(); child.terminate();
} }
child.wait();
}, },
Signal::Stop => { Signal::Stop => child.pause(),
child.pause(); Signal::Continue => child.resume(),
}, Signal::ChildExit => child.reap(),
Signal::Continue => {
child.resume();
}
} }
} }
} }
@ -153,6 +148,7 @@ fn main() {
} }
loop { loop {
debug!("Waiting for filesystem activity");
let paths = wait(&rx, &filter); let paths = wait(&rx, &filter);
if let Some(path) = paths.get(0) { if let Some(path) = paths.get(0) {
debug!("Path updated: {:?}", path); debug!("Path updated: {:?}", path);
@ -182,6 +178,7 @@ fn main() {
cli::clear_screen(); cli::clear_screen();
} }
debug!("Launching child process");
{ {
let mut guard = child_process.write().unwrap(); let mut guard = child_process.write().unwrap();
*guard = Some(process::spawn(&args.cmd, paths)); *guard = Some(process::spawn(&args.cmd, paths));

View File

@ -12,9 +12,12 @@ mod imp {
use std::io::Result; use std::io::Result;
use std::path::PathBuf; use std::path::PathBuf;
use std::process::Command; use std::process::Command;
use std::sync::*;
pub struct Process { pub struct Process {
pgid: pid_t, pgid: pid_t,
lock: Mutex<bool>,
cvar: Condvar,
} }
impl Process { impl Process {
@ -55,7 +58,11 @@ mod imp {
} }
let _ = close(w); let _ = close(w);
Ok(Process { pgid: child }) Ok(Process {
pgid: child,
lock: Mutex::new(false),
cvar: Condvar::new(),
})
} }
Ok(ForkResult::Child) => { Ok(ForkResult::Child) => {
let _ = setpgid(0, 0); let _ = setpgid(0, 0);
@ -85,6 +92,25 @@ mod imp {
self.signal(SIGTSTP); 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) { pub fn resume(&self) {
self.signal(SIGCONT); self.signal(SIGCONT);
@ -106,9 +132,10 @@ mod imp {
} }
pub fn wait(&self) { pub fn wait(&self) {
use nix::sys::wait::waitpid; let mut done = self.lock.lock().unwrap();
while !*done {
while let Ok(_) = waitpid(-self.pgid, None) {} done = self.cvar.wait(done).unwrap();
}
} }
} }
} }
@ -181,6 +208,9 @@ mod imp {
pub fn pause(&self) { pub fn pause(&self) {
} }
pub fn reap(&self) {
}
pub fn resume(&self) { pub fn resume(&self) {
} }
@ -190,7 +220,6 @@ mod imp {
} }
} }
pub fn wait(&self) { pub fn wait(&self) {
unsafe { unsafe {
let _ = WaitForSingleObject(self.job, INFINITE); let _ = WaitForSingleObject(self.job, INFINITE);

View File

@ -7,7 +7,8 @@ lazy_static! {
pub enum Signal { pub enum Signal {
Terminate, Terminate,
Stop, Stop,
Continue Continue,
ChildExit
} }
#[cfg(unix)] #[cfg(unix)]
@ -15,6 +16,7 @@ pub fn install_handler<F>(handler: F)
where F: Fn(self::Signal) + 'static + Send + Sync where F: Fn(self::Signal) + 'static + Send + Sync
{ {
use std::thread; use std::thread;
use libc::c_int;
use nix::sys::signal::*; use nix::sys::signal::*;
// Mask all signals interesting to us. The mask propagates // Mask all signals interesting to us. The mask propagates
@ -24,20 +26,32 @@ pub fn install_handler<F>(handler: F)
mask.add(SIGINT); mask.add(SIGINT);
mask.add(SIGTSTP); mask.add(SIGTSTP);
mask.add(SIGCONT); mask.add(SIGCONT);
mask.add(SIGCHLD);
mask.thread_set_mask().expect("unable to set signal mask"); mask.thread_set_mask().expect("unable to set signal mask");
set_handler(handler); 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 // Spawn a thread to catch these signals
thread::spawn(move || { thread::spawn(move || {
loop { loop {
let raw_signal = mask.wait().expect("unable to sigwait"); let raw_signal = mask.wait().expect("unable to sigwait");
debug!("Received {:?}", raw_signal);
let sig = match raw_signal { let sig = match raw_signal {
SIGTERM => self::Signal::Terminate, SIGTERM => self::Signal::Terminate,
SIGINT => self::Signal::Terminate, SIGINT => self::Signal::Terminate,
SIGTSTP => self::Signal::Stop, SIGTSTP => self::Signal::Stop,
SIGCONT => self::Signal::Continue, SIGCONT => self::Signal::Continue,
SIGCHLD => self::Signal::ChildExit,
_ => unreachable!() _ => unreachable!()
}; };
@ -45,10 +59,12 @@ pub fn install_handler<F>(handler: F)
invoke(sig); invoke(sig);
// Restore default behavior for received signal and unmask it // 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 { unsafe {
let _ = sigaction(raw_signal, &default_action); let _ = sigaction(raw_signal, &default_action);
}
} }
let mut new_mask = SigSet::empty(); let mut new_mask = SigSet::empty();