Update process to use std::future (#1343)

This commit is contained in:
andy finch
2019-07-29 18:36:11 -07:00
committed by Ivan Petkov
parent 74168ae82f
commit fbf90e6356
10 changed files with 331 additions and 206 deletions
+124 -80
View File
@@ -13,13 +13,12 @@
//! for it using an event loop.
//!
//! ```no_run
//! extern crate futures;
//! extern crate tokio;
//! extern crate tokio_process;
//!
//! use std::process::Command;
//!
//! use futures::Future;
//! use futures_util::future::FutureExt;
//! use tokio_process::CommandExt;
//!
//! fn main() {
@@ -42,13 +41,12 @@
//! world` but we also capture its output.
//!
//! ```no_run
//! extern crate futures;
//! extern crate tokio;
//! extern crate tokio_process;
//!
//! use std::process::Command;
//!
//! use futures::Future;
//! use futures_util::future::FutureExt;
//! use tokio_process::CommandExt;
//!
//! fn main() {
@@ -71,13 +69,13 @@
//!
//! ```no_run
//! extern crate failure;
//! extern crate futures;
//! extern crate tokio;
//! extern crate tokio_process;
//! extern crate tokio_io;
//!
//! use failure::Error;
//! use futures::{Future, Stream};
//! use futures_util::future::FutureExt;
//! use futures_util::stream::StreamExt;
//! use std::io::BufReader;
//! use std::process::{Command, Stdio};
//! use tokio_process::{Child, ChildStdout, CommandExt};
@@ -157,8 +155,8 @@
#![warn(missing_debug_implementations)]
#![deny(missing_docs)]
#![doc(html_root_url = "https://docs.rs/tokio-process/0.2")]
#![feature(async_await)]
extern crate futures;
extern crate tokio_io;
extern crate tokio_reactor;
@@ -172,12 +170,20 @@ extern crate log;
use std::io::{self, Read, Write};
use std::process::{Command, ExitStatus, Output, Stdio};
use crate::kill::Kill;
use futures::future::{ok, Either};
use futures::{Async, Future, IntoFuture, Poll};
use futures_core::future::TryFuture;
use futures_util::future;
use futures_util::future::FutureExt;
use futures_util::io::{AsyncRead, AsyncWrite};
use futures_util::try_future::TryFutureExt;
use kill::Kill;
use std::fmt;
use tokio_io::io::read_to_end;
use tokio_io::{AsyncRead, AsyncWrite, IoFuture};
use std::future::Future;
use std::pin::Pin;
use std::task::Context;
use std::task::Poll;
use tokio_io::AsyncRead as TokioAsyncRead;
use tokio_io::AsyncWrite as TokioAsyncWrite;
use tokio_reactor::Handle;
#[path = "unix/mod.rs"]
@@ -362,13 +368,11 @@ impl CommandExt for Command {
self.stdout(Stdio::piped());
self.stderr(Stdio::piped());
let inner = self
.spawn_async_with_handle(handle)
.into_future()
.and_then(Child::wait_with_output);
let inner =
future::ready(self.spawn_async_with_handle(handle)).and_then(Child::wait_with_output);
OutputAsync {
inner: Box::new(inner),
inner: inner.boxed(),
}
}
}
@@ -414,16 +418,16 @@ impl<T: Kill> Drop for ChildDropGuard<T> {
}
}
impl<T: Future + Kill> Future for ChildDropGuard<T> {
type Item = T::Item;
type Error = T::Error;
impl<T: TryFuture + Kill + Unpin> Future for ChildDropGuard<T> {
type Output = Result<T::Ok, T::Error>;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
let ret = self.inner.poll();
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
let inner = Pin::get_mut(self);
let ret = inner.inner.try_poll_unpin(cx);
if let Ok(Async::Ready(_)) = ret {
if let Poll::Ready(Ok(_)) = ret {
// Avoid the overhead of trying to kill a reaped process
self.kill_on_drop = false;
inner.kill_on_drop = false;
}
ret
@@ -501,24 +505,39 @@ impl Child {
/// `stderr(Stdio::piped())`, respectively, when creating a `Command`.
pub fn wait_with_output(mut self) -> WaitWithOutput {
drop(self.stdin().take());
let stdout = match self.stdout().take() {
Some(io) => Either::A(read_to_end(io, Vec::new()).map(|p| p.1)),
None => Either::B(ok(Vec::new())),
let stdout_val = self.stdout.take();
let stderr_val = self.stderr.take();
let stdout_fut = async {
match stdout_val {
Some(mut io) => {
let mut vec = Vec::new();
futures_util::io::AsyncReadExt::read_to_end(&mut io, &mut vec).await?;
Ok(vec)
}
None => Ok(Vec::new()),
}
};
let stderr = match self.stderr().take() {
Some(io) => Either::A(read_to_end(io, Vec::new()).map(|p| p.1)),
None => Either::B(ok(Vec::new())),
let stderr_fut = async {
match stderr_val {
Some(mut io) => {
let mut vec = Vec::new();
futures_util::io::AsyncReadExt::read_to_end(&mut io, &mut vec).await?;
Ok(vec)
}
None => Ok(Vec::new()),
}
};
WaitWithOutput {
inner: Box::new(
self.join3(stdout, stderr)
.map(|(status, stdout, stderr)| Output {
inner: futures_util::try_future::try_join3(stdout_fut, stderr_fut, self)
.and_then(|(stdout, stderr, status)| {
future::ok(Output {
status,
stdout,
stderr,
}),
),
})
})
.boxed(),
}
}
@@ -533,13 +552,12 @@ impl Child {
/// > `Child` instance into an event loop as an alternative to this method.
///
/// ```no_run
/// # extern crate futures;
/// # extern crate tokio;
/// # extern crate tokio_process;
/// #
/// # use std::process::Command;
/// #
/// # use futures::Future;
/// # use futures_util::future::FutureExt;
/// # use tokio_process::CommandExt;
/// #
/// # fn main() {
@@ -559,11 +577,10 @@ impl Child {
}
impl Future for Child {
type Item = ExitStatus;
type Error = io::Error;
type Output = io::Result<ExitStatus>;
fn poll(&mut self) -> Poll<ExitStatus, io::Error> {
self.child.poll()
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
Pin::get_mut(self).child.poll_unpin(cx)
}
}
@@ -573,7 +590,7 @@ impl Future for Child {
/// contains the exit status, stdout, and stderr of a child process.
#[must_use = "futures do nothing unless polled"]
pub struct WaitWithOutput {
inner: IoFuture<Output>,
inner: Pin<Box<dyn Future<Output = io::Result<Output>> + Send>>,
}
impl fmt::Debug for WaitWithOutput {
@@ -585,11 +602,10 @@ impl fmt::Debug for WaitWithOutput {
}
impl Future for WaitWithOutput {
type Item = Output;
type Error = io::Error;
type Output = io::Result<Output>;
fn poll(&mut self) -> Poll<Output, io::Error> {
self.inner.poll()
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
Pin::get_mut(self).inner.poll_unpin(cx)
}
}
@@ -609,11 +625,10 @@ pub struct StatusAsync {
}
impl Future for StatusAsync {
type Item = ExitStatus;
type Error = io::Error;
type Output = io::Result<ExitStatus>;
fn poll(&mut self) -> Poll<ExitStatus, io::Error> {
self.inner.poll()
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
Pin::get_mut(self).inner.poll_unpin(cx)
}
}
@@ -624,7 +639,7 @@ impl Future for StatusAsync {
/// process, collecting all of its output and its exit status.
#[must_use = "futures do nothing unless polled"]
pub struct OutputAsync {
inner: IoFuture<Output>,
inner: Pin<Box<dyn Future<Output = io::Result<Output>> + Send>>,
}
impl fmt::Debug for OutputAsync {
@@ -636,11 +651,10 @@ impl fmt::Debug for OutputAsync {
}
impl Future for OutputAsync {
type Item = Output;
type Error = io::Error;
type Output = io::Result<Output>;
fn poll(&mut self) -> Poll<Output, io::Error> {
self.inner.poll()
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
Pin::get_mut(self).inner.poll_unpin(cx)
}
}
@@ -678,35 +692,59 @@ pub struct ChildStderr {
impl Write for ChildStdin {
fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
self.inner.write(bytes)
self.inner.get_mut().write(bytes)
}
fn flush(&mut self) -> io::Result<()> {
self.inner.flush()
self.inner.get_mut().flush()
}
}
impl AsyncWrite for ChildStdin {
fn shutdown(&mut self) -> Poll<(), io::Error> {
self.inner.shutdown()
fn poll_write(self: Pin<&mut Self>, cx: &mut Context, buf: &[u8]) -> Poll<io::Result<usize>> {
Pin::new(&mut Pin::get_mut(self).inner).poll_write(cx, buf)
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll<io::Result<()>> {
Pin::new(&mut Pin::get_mut(self).inner).poll_flush(cx)
}
fn poll_close(self: Pin<&mut Self>, cx: &mut Context) -> Poll<io::Result<()>> {
Pin::new(&mut Pin::get_mut(self).inner).poll_shutdown(cx)
}
}
impl Read for ChildStdout {
fn read(&mut self, bytes: &mut [u8]) -> io::Result<usize> {
self.inner.read(bytes)
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.inner.get_mut().read(buf)
}
}
impl AsyncRead for ChildStdout {}
impl AsyncRead for ChildStdout {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context,
buf: &mut [u8],
) -> Poll<io::Result<usize>> {
Pin::new(&mut Pin::get_mut(self).inner).poll_read(cx, buf)
}
}
impl Read for ChildStderr {
fn read(&mut self, bytes: &mut [u8]) -> io::Result<usize> {
self.inner.read(bytes)
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.inner.get_mut().read(buf)
}
}
impl AsyncRead for ChildStderr {}
impl AsyncRead for ChildStderr {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context,
buf: &mut [u8],
) -> Poll<io::Result<usize>> {
Pin::new(&mut Pin::get_mut(self).inner).poll_read(cx, buf)
}
}
#[cfg(unix)]
mod sys {
@@ -760,21 +798,25 @@ mod sys {
mod test {
use super::ChildDropGuard;
use crate::kill::Kill;
use futures::{Async, Future, Poll};
use futures_util::future::FutureExt;
use std::future::Future;
use std::io;
use std::pin::Pin;
use std::task::Context;
use std::task::Poll;
struct Mock {
num_kills: usize,
num_polls: usize,
poll_result: Poll<(), ()>,
poll_result: Poll<Result<(), ()>>,
}
impl Mock {
fn new() -> Self {
Self::with_result(Ok(Async::NotReady))
Self::with_result(Poll::Pending)
}
fn with_result(result: Poll<(), ()>) -> Self {
fn with_result(result: Poll<Result<(), ()>>) -> Self {
Self {
num_kills: 0,
num_polls: 0,
@@ -791,12 +833,12 @@ mod test {
}
impl Future for Mock {
type Item = ();
type Error = ();
type Output = Result<(), ()>;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
self.num_polls += 1;
self.poll_result
fn poll(self: Pin<&mut Self>, _cx: &mut Context) -> Poll<Self::Output> {
let inner = Pin::get_mut(self);
inner.num_polls += 1;
inner.poll_result
}
}
@@ -829,19 +871,21 @@ mod test {
#[test]
fn no_kill_if_reaped() {
let mut mock_pending = Mock::with_result(Ok(Async::NotReady));
let mut mock_reaped = Mock::with_result(Ok(Async::Ready(())));
let mut mock_err = Mock::with_result(Err(()));
let mut mock_pending = Mock::with_result(Poll::Pending);
let mut mock_reaped = Mock::with_result(Poll::Ready(Ok(())));
let mut mock_err = Mock::with_result(Poll::Ready(Err(())));
let waker = futures_util::task::noop_waker();
let mut context = Context::from_waker(&waker);
{
let mut guard = ChildDropGuard::new(&mut mock_pending);
let _ = guard.poll();
let _ = guard.poll_unpin(&mut context);
let mut guard = ChildDropGuard::new(&mut mock_reaped);
let _ = guard.poll();
let _ = guard.poll_unpin(&mut context);
let mut guard = ChildDropGuard::new(&mut mock_err);
let _ = guard.poll();
let _ = guard.poll_unpin(&mut context);
}
assert_eq!(1, mock_pending.num_kills);
+40 -22
View File
@@ -33,17 +33,23 @@ use self::mio::unix::{EventedFd, UnixReady};
use self::mio::{Poll as MioPoll, PollOpt, Ready, Token};
use self::orphan::{AtomicOrphanQueue, OrphanQueue, Wait};
use self::reap::Reaper;
use self::tokio_signal::unix::Signal;
use super::SpawnedChild;
use crate::kill::Kill;
use futures::future::FlattenStream;
use futures::{Future, Poll};
use futures_core::stream::Stream;
use futures_util::future;
use futures_util::future::FutureExt;
use futures_util::stream::StreamExt;
use futures_util::try_future::TryFutureExt;
use std::fmt;
use std::future::Future;
use std::io;
use std::os::unix::io::{AsRawFd, RawFd};
use std::pin::Pin;
use std::process::{self, ExitStatus};
use tokio_io::IoFuture;
use std::task::Context;
use std::task::Poll;
use tokio_reactor::{Handle, PollEvented};
use tokio_signal::unix::Signal;
impl Wait for process::Child {
fn id(&self) -> u32 {
@@ -83,9 +89,11 @@ impl OrphanQueue<process::Child> for GlobalOrphanQueue {
}
}
type ChildReaperFuture = Pin<Box<dyn Stream<Item = io::Result<()>> + Send>>;
#[must_use = "futures do nothing unless polled"]
pub struct Child {
inner: Reaper<process::Child, GlobalOrphanQueue, FlattenStream<IoFuture<Signal>>>,
inner: Reaper<process::Child, GlobalOrphanQueue, ChildReaperFuture>,
}
impl fmt::Debug for Child {
@@ -102,7 +110,10 @@ pub(crate) fn spawn_child(cmd: &mut process::Command, handle: &Handle) -> io::Re
let stdout = stdio(child.stdout.take(), handle)?;
let stderr = stdio(child.stderr.take(), handle)?;
let signal = Signal::with_handle(libc::SIGCHLD, handle).flatten_stream();
let signal = Signal::with_handle(libc::SIGCHLD, handle)
.and_then(|stream| future::ok(stream.map(Ok)))
.try_flatten_stream()
.boxed();
Ok(SpawnedChild {
child: Child {
inner: Reaper::new(child, GlobalOrphanQueue, signal),
@@ -126,30 +137,37 @@ impl Kill for Child {
}
impl Future for Child {
type Item = ExitStatus;
type Error = io::Error;
type Output = io::Result<ExitStatus>;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
self.inner.poll()
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
(&mut Pin::get_mut(self).inner).poll_unpin(cx)
}
}
#[derive(Debug)]
pub struct Fd<T>(T);
pub struct Fd<T> {
inner: T,
}
impl<T: io::Read> io::Read for Fd<T> {
impl<T> io::Read for Fd<T>
where
T: io::Read,
{
fn read(&mut self, bytes: &mut [u8]) -> io::Result<usize> {
self.0.read(bytes)
self.inner.read(bytes)
}
}
impl<T: io::Write> io::Write for Fd<T> {
impl<T> io::Write for Fd<T>
where
T: io::Write,
{
fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
self.0.write(bytes)
self.inner.write(bytes)
}
fn flush(&mut self) -> io::Result<()> {
self.0.flush()
self.inner.flush()
}
}
@@ -158,14 +176,10 @@ where
T: AsRawFd,
{
fn as_raw_fd(&self) -> RawFd {
self.0.as_raw_fd()
self.inner.as_raw_fd()
}
}
pub type ChildStdin = PollEvented<Fd<process::ChildStdin>>;
pub type ChildStdout = PollEvented<Fd<process::ChildStdout>>;
pub type ChildStderr = PollEvented<Fd<process::ChildStderr>>;
impl<T> Evented for Fd<T>
where
T: AsRawFd,
@@ -195,6 +209,10 @@ where
}
}
pub type ChildStdin = PollEvented<Fd<process::ChildStdin>>;
pub type ChildStdout = PollEvented<Fd<process::ChildStdout>>;
pub type ChildStderr = PollEvented<Fd<process::ChildStderr>>;
fn stdio<T>(option: Option<T>, handle: &Handle) -> io::Result<Option<PollEvented<Fd<T>>>>
where
T: AsRawFd,
@@ -216,6 +234,6 @@ where
return Err(io::Error::last_os_error());
}
}
let io = PollEvented::new_with_handle(Fd(io), handle)?;
let io = PollEvented::new_with_handle(Fd { inner: io }, handle)?;
Ok(Some(io))
}
+50 -28
View File
@@ -1,16 +1,21 @@
use super::orphan::{OrphanQueue, Wait};
use crate::kill::Kill;
use futures::{Async, Future, Poll, Stream};
use futures_core::stream::TryStream;
use futures_util::try_stream::TryStreamExt;
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,
W: Wait + Unpin,
Q: OrphanQueue<W>,
{
inner: Option<W>,
@@ -20,7 +25,7 @@ where
impl<W, Q, S> Deref for Reaper<W, Q, S>
where
W: Wait,
W: Wait + Unpin,
Q: OrphanQueue<W>,
{
type Target = W;
@@ -32,7 +37,7 @@ where
impl<W, Q, S> Reaper<W, Q, S>
where
W: Wait,
W: Wait + Unpin,
Q: OrphanQueue<W>,
{
pub(crate) fn new(inner: W, orphan_queue: Q, signal: S) -> Self {
@@ -54,14 +59,14 @@ where
impl<W, Q, S> Future for Reaper<W, Q, S>
where
W: Wait,
Q: OrphanQueue<W>,
S: Stream<Error = io::Error>,
W: Wait + Unpin,
Q: OrphanQueue<W> + Unpin,
S: TryStream<Error = io::Error> + Unpin,
{
type Item = ExitStatus;
type Error = io::Error;
type Output = io::Result<ExitStatus>;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
let inner = Pin::get_mut(self);
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
@@ -82,17 +87,21 @@ where
// 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()?.is_not_ready();
let signal_poll = inner.signal.try_poll_next_unpin(cx);
if let Poll::Ready(Some(Err(err))) = signal_poll {
return Poll::Ready(Err(err));
}
let registered_interest = signal_poll.is_pending();
self.orphan_queue.reap_orphans();
if let Some(status) = self.inner_mut().try_wait()? {
return Ok(Async::Ready(status));
inner.orphan_queue.reap_orphans();
if let Some(status) = inner.inner_mut().try_wait()? {
return Poll::Ready(Ok(status));
}
// 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 Ok(Async::NotReady);
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
@@ -105,7 +114,7 @@ where
impl<W, Q, S> Kill for Reaper<W, Q, S>
where
W: Kill + Wait,
W: Kill + Wait + Unpin,
Q: OrphanQueue<W>,
{
fn kill(&mut self) -> io::Result<()> {
@@ -115,7 +124,7 @@ where
impl<W, Q, S> Drop for Reaper<W, Q, S>
where
W: Wait,
W: Wait + Unpin,
Q: OrphanQueue<W>,
{
fn drop(&mut self) {
@@ -131,10 +140,14 @@ where
#[cfg(test)]
mod test {
use super::*;
use futures::{Async, Poll, Stream};
use futures_core::stream::Stream;
use futures_util::future::FutureExt;
use std::cell::{Cell, RefCell};
use std::os::unix::process::ExitStatusExt;
use std::pin::Pin;
use std::process::ExitStatus;
use std::task::Context;
use std::task::Poll;
#[derive(Debug)]
struct MockWait {
@@ -194,14 +207,14 @@ mod test {
}
impl Stream for MockStream {
type Item = ();
type Error = io::Error;
type Item = io::Result<()>;
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
self.total_polls += 1;
match self.values.remove(0) {
Some(()) => Ok(Async::Ready(Some(()))),
None => Ok(Async::NotReady),
fn poll_next(self: Pin<&mut Self>, _cx: &mut Context) -> Poll<Option<Self::Item>> {
let inner = Pin::get_mut(self);
inner.total_polls += 1;
match inner.values.remove(0) {
Some(()) => Poll::Ready(Some(Ok(()))),
None => Poll::Pending,
}
}
}
@@ -240,8 +253,11 @@ mod test {
MockStream::new(vec![None, Some(()), None, None, None]),
);
let waker = futures_util::task::noop_waker();
let mut context = Context::from_waker(&waker);
// Not yet exited, interest registered
assert_eq!(Async::NotReady, grim.poll().expect("failed to wait"));
assert!(grim.poll_unpin(&mut context).is_pending());
assert_eq!(1, grim.signal.total_polls);
assert_eq!(1, grim.total_waits);
assert_eq!(1, grim.orphan_queue.total_reaps.get());
@@ -249,14 +265,20 @@ mod test {
// Not yet exited, couldn't register interest the first time
// but managed to register interest the second time around
assert_eq!(Async::NotReady, grim.poll().expect("failed to wait"));
assert!(grim.poll_unpin(&mut context).is_pending());
assert_eq!(3, grim.signal.total_polls);
assert_eq!(3, grim.total_waits);
assert_eq!(3, grim.orphan_queue.total_reaps.get());
assert!(grim.orphan_queue.all_enqueued.borrow().is_empty());
// Exited
assert_eq!(Async::Ready(exit), grim.poll().expect("failed to wait"));
if let Poll::Ready(r) = grim.poll_unpin(&mut context) {
assert!(r.is_ok());
let exit_code = r.unwrap();
assert_eq!(exit_code, exit);
} else {
unreachable!();
}
assert_eq!(4, grim.signal.total_polls);
assert_eq!(4, grim.total_waits);
assert_eq!(4, grim.orphan_queue.total_reaps.get());
+25 -18
View File
@@ -18,12 +18,21 @@
extern crate mio_named_pipes;
extern crate winapi;
use crate::kill::Kill;
use std::fmt;
use std::future::Future;
use std::io;
use std::os::windows::prelude::*;
use std::os::windows::process::ExitStatusExt;
use std::pin::Pin;
use std::process::{self, ExitStatus};
use std::ptr;
use std::task::Context;
use std::task::Poll;
use futures_util::future::Fuse;
use futures_util::future::FutureExt;
use self::mio_named_pipes::NamedPipe;
use self::winapi::shared::minwindef::*;
@@ -35,11 +44,8 @@ use self::winapi::um::threadpoollegacyapiset::*;
use self::winapi::um::winbase::*;
use self::winapi::um::winnt::*;
use super::SpawnedChild;
use futures::future::Fuse;
use futures::sync::oneshot;
use futures::{Async, Future, Poll};
use kill::Kill;
use tokio_reactor::{Handle, PollEvented};
use tokio_sync::oneshot;
#[must_use = "futures do nothing unless polled"]
pub struct Child {
@@ -96,22 +102,23 @@ impl Kill for Child {
}
impl Future for Child {
type Item = ExitStatus;
type Error = io::Error;
type Output = io::Result<ExitStatus>;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
let inner = Pin::get_mut(self);
loop {
if let Some(ref mut w) = self.waiting {
match w.rx.poll().expect("should not be canceled") {
Async::Ready(()) => {}
Async::NotReady => return Ok(Async::NotReady),
if let Some(ref mut w) = inner.waiting {
match w.rx.poll_unpin(cx) {
Poll::Ready(Ok(())) => {}
Poll::Ready(Err(_)) => panic!("should not be canceled"),
Poll::Pending => return Poll::Pending,
}
let status = try_wait(&self.child)?.expect("not ready yet");
return Ok(status.into());
let status = try_wait(&inner.child)?.expect("not ready yet");
return Poll::Ready(Ok(status.into()));
}
if let Some(e) = try_wait(&self.child)? {
return Ok(e.into());
if let Some(e) = try_wait(&inner.child)? {
return Poll::Ready(Ok(e.into()));
}
let (tx, rx) = oneshot::channel();
let ptr = Box::into_raw(Box::new(Some(tx)));
@@ -119,7 +126,7 @@ impl Future for Child {
let rc = unsafe {
RegisterWaitForSingleObject(
&mut wait_object,
self.child.as_raw_handle(),
inner.child.as_raw_handle(),
Some(callback),
ptr as *mut _,
INFINITE,
@@ -129,9 +136,9 @@ impl Future for Child {
if rc == 0 {
let err = io::Error::last_os_error();
drop(unsafe { Box::from_raw(ptr) });
return Err(err);
return Poll::Ready(Err(err));
}
self.waiting = Some(Waiting {
inner.waiting = Some(Waiting {
rx: rx.fuse(),
wait_object,
tx: ptr,