use std::future::Future; use std::io; use std::ops::Deref; use std::pin::Pin; use std::process::ExitStatus; use std::task::Context; use std::task::Poll;
/// Orchestrates between registering interest for receiving signals when a /// child process has exited, and attempting to poll for process completion. #[derive(Debug)] pub(crate) struct Reaper<W, Q, S> where
W: Wait,
Q: OrphanQueue<W>,
{
inner: Option<W>,
orphan_queue: Q,
signal: S,
}
impl<W, Q, S> Deref for Reaper<W, Q, S> where
W: Wait,
Q: OrphanQueue<W>,
{ type Target = W;
impl<W, Q, S> Future for Reaper<W, Q, S> where
W: Wait + Unpin,
Q: OrphanQueue<W> + Unpin,
S: InternalStream + Unpin,
{ type Output = io::Result<ExitStatus>;
fn poll(mutself: Pin<&mutSelf>, cx: &mut Context<'_>) -> Poll<Self::Output> { loop { // 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 // make progress. We can use the delivery of a SIGCHLD signal as a // sign that we can potentially make progress. // // However, we will register for a notification on the next signal // BEFORE we poll the child. Otherwise it is possible that the child // can exit and the signal can arrive after we last polled the child, // but before we've registered for a notification on the next signal // (this can cause a deadlock if there are no more spawned children // which can generate a different signal for us). A side effect of // pre-registering for signal notifications is that when the child // exits, we will have already registered for an additional // notification we don't need to consume. If another signal arrives, // this future's task will be notified/woken up again. Since the // futures model allows for spurious wake ups this extra wakeup // should not cause significant issues with parent futures. let registered_interest = self.signal.poll_recv(cx).is_pending();
// If our attempt to poll for the next signal was not ready, then // we've arranged for our task to get notified and we can bail out. if registered_interest { return Poll::Pending;
} else { // Otherwise, if the signal stream delivered a signal to us, we // won't get notified at the next signal, so we'll loop and try // again. continue;
}
}
}
}
impl<W, Q, S> Kill for Reaper<W, Q, S> where
W: Kill + Wait,
Q: OrphanQueue<W>,
{ fn kill(&mutself) -> io::Result<()> { self.inner_mut().kill()
}
}
impl<W, Q, S> Drop for Reaper<W, Q, S> where
W: Wait,
Q: OrphanQueue<W>,
{ fn drop(&mutself) { iflet Ok(Some(_)) = self.inner_mut().try_wait() { return;
}
let orphan = self.inner.take().unwrap(); self.orphan_queue.push_orphan(orphan);
}
}
#[cfg(all(test, not(loom)))] mod test { usesuper::*;
usecrate::process::unix::orphan::test::MockQueue; use futures::future::FutureExt; use std::os::unix::process::ExitStatusExt; use std::process::ExitStatus; use std::task::Context; use std::task::Poll;
// Not yet exited, couldn't register interest the first time // but managed to register interest the second time around
assert!(grim.poll_unpin(&mut context).is_pending());
assert_eq!(3, grim.signal.total_polls);
assert_eq!(3, grim.total_waits);
assert!(grim.orphan_queue.all_enqueued.borrow().is_empty());
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.