process: Optimize out the "reaped" flag

This commit is contained in:
Ivan Petkov 2019-05-25 15:36:47 -07:00
parent db0c4147c8
commit 42d0f53ddb
No known key found for this signature in database
GPG Key ID: 0B431E9837056942

View File

@ -15,78 +15,11 @@ pub trait Kill {
fn kill(&mut self) -> io::Result<()>; fn kill(&mut self) -> io::Result<()>;
} }
#[derive(Debug, PartialEq)]
enum WaitResult {
Exited(ExitStatus),
Reaped,
}
/// An interface for safely reaping a child process.
trait Reap {
/// Try to reap the child process if ready.
fn try_reap(&mut self) -> Poll<WaitResult, io::Error>;
}
#[derive(Debug)]
struct Reaper<W> {
reaped: bool,
proc: W,
}
impl<W> Reaper<W> {
fn new(proc: W) -> Self {
Self {
reaped: false,
proc,
}
}
fn reaped(&self) -> bool {
self.reaped
}
}
impl<W> Deref for Reaper<W> {
type Target = W;
fn deref(&self) -> &Self::Target {
&self.proc
}
}
impl<W: Wait> Reap for Reaper<W> {
fn try_reap(&mut self) -> Poll<WaitResult, io::Error> {
if self.reaped {
return Ok(Async::Ready(WaitResult::Reaped));
}
match self.proc.try_wait()? {
Some(exit) => {
self.reaped = true;
Ok(Async::Ready(WaitResult::Exited(exit)))
},
None => Ok(Async::NotReady),
}
}
}
impl<W: Kill> Kill for Reaper<W> {
fn kill(&mut self) -> io::Result<()> {
// NB: ensure we don't issue a kill after we've reaped the child
// since its process identifier could have been reused.
if self.reaped {
Ok(())
} else {
self.proc.kill()
}
}
}
/// Orchestrates between registering interest for receiving signals when a /// Orchestrates between registering interest for receiving signals when a
/// child process has exited, and attempting to poll for process completion. /// child process has exited, and attempting to poll for process completion.
#[derive(Debug)] #[derive(Debug)]
pub struct EventedReaper<W, S> { pub struct EventedReaper<W, S> {
inner: Reaper<W>, inner: W,
signal: S, signal: S,
} }
@ -94,14 +27,14 @@ impl<W, S> Deref for EventedReaper<W, S> {
type Target = W; type Target = W;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&*self.inner &self.inner
} }
} }
impl<W, S> EventedReaper<W, S> { impl<W, S> EventedReaper<W, S> {
pub fn new(inner: W, signal: S) -> Self { pub fn new(inner: W, signal: S) -> Self {
Self { Self {
inner: Reaper::new(inner), inner,
signal, signal,
} }
} }
@ -116,12 +49,6 @@ impl<W, S> Future for EventedReaper<W, S>
fn poll(&mut self) -> Poll<Self::Item, Self::Error> { fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
loop { loop {
// Ensure we don't register for additional notifications
// if the child has already finished.
if self.inner.reaped() {
return Ok(Async::NotReady);
}
// If the child hasn't exited yet, then it's our responsibility to // If the child hasn't exited yet, then it's our responsibility to
// ensure the current task gets notified when it might be able to // ensure the current task gets notified when it might be able to
// make progress. // make progress.
@ -143,7 +70,7 @@ impl<W, S> Future for EventedReaper<W, S>
// should not cause significant issues with parent futures. // should not cause significant issues with parent futures.
let registered_interest = self.signal.poll()?.is_not_ready(); let registered_interest = self.signal.poll()?.is_not_ready();
if let Async::Ready(WaitResult::Exited(status)) = self.inner.try_reap()? { if let Some(status) = self.inner.try_wait()? {
return Ok(Async::Ready(status)); return Ok(Async::Ready(status));
} }
@ -241,25 +168,6 @@ mod test {
} }
} }
#[test]
fn reaper() {
let exit = ExitStatus::from_raw(0);
let mock = MockWait::new(exit.clone(), 1);
let mut grim = Reaper::new(mock);
// Not yet exited
assert_eq!(Async::NotReady, grim.try_reap().expect("failed to wait"));
assert_eq!(1, grim.total_waits);
// Exited
assert_eq!(Async::Ready(WaitResult::Exited(exit)), grim.try_reap().expect("failed to wait"));
assert_eq!(2, grim.total_waits);
// Cannot call wait another time
assert_eq!(Async::Ready(WaitResult::Reaped), grim.try_reap().expect("failed to wait"));
assert_eq!(2, grim.total_waits);
}
#[test] #[test]
fn evented_reaper() { fn evented_reaper() {
let exit = ExitStatus::from_raw(0); let exit = ExitStatus::from_raw(0);
@ -287,11 +195,6 @@ mod test {
assert_eq!(Async::Ready(exit), grim.poll().expect("failed to wait")); assert_eq!(Async::Ready(exit), grim.poll().expect("failed to wait"));
assert_eq!(4, grim.signal.total_polls); assert_eq!(4, grim.signal.total_polls);
assert_eq!(4, grim.total_waits); assert_eq!(4, grim.total_waits);
// Already reaped, no further calls
assert_eq!(Async::NotReady, grim.poll().expect("failed to poll"));
assert_eq!(4, grim.signal.total_polls);
assert_eq!(4, grim.total_waits);
} }
#[test] #[test]
@ -304,10 +207,5 @@ mod test {
grim.kill().unwrap(); grim.kill().unwrap();
assert_eq!(1, grim.total_kills); assert_eq!(1, grim.total_kills);
// Do not kill after reaping
assert_eq!(Async::Ready(exit), grim.poll().expect("failed to poll"));
grim.kill().unwrap();
assert_eq!(1, grim.total_kills);
} }
} }