mirror of
https://github.com/tokio-rs/tokio.git
synced 2025-09-28 12:10:37 +00:00
process: Add status_async2
as a closer analog to spawn_async
This commit is contained in:
parent
56d3914675
commit
b9c6eb309c
58
src/lib.rs
58
src/lib.rs
@ -186,8 +186,27 @@ pub trait CommandExt {
|
||||
///
|
||||
/// If the `StatusAsync` future is dropped before the future resolves, then
|
||||
/// the child will be killed, if it was spawned.
|
||||
#[deprecated(note = "use the more flexible `spawn_async2` method instead")]
|
||||
#[allow(deprecated)]
|
||||
fn status_async(&mut self, handle: &Handle) -> StatusAsync;
|
||||
|
||||
/// Executes a command as a child process, waiting for it to finish and
|
||||
/// collecting its exit status.
|
||||
///
|
||||
/// By default, stdin, stdout and stderr are inherited from the parent.
|
||||
///
|
||||
/// The `StatusAsync` future returned will resolve to the `ExitStatus`
|
||||
/// type in the standard library representing how the process exited. If
|
||||
/// any input/output handles are set to a pipe then they will be immediately
|
||||
/// closed after the child is spawned.
|
||||
///
|
||||
/// The `handle` specified must be a handle to a valid event loop, and all
|
||||
/// I/O this child does will be associated with the specified event loop.
|
||||
///
|
||||
/// If the `StatusAsync` future is dropped before the future resolves, then
|
||||
/// the child will be killed, if it was spawned.
|
||||
fn status_async2(&mut self, handle: &Handle) -> io::Result<StatusAsync2>;
|
||||
|
||||
/// Executes the command as a child process, waiting for it to finish and
|
||||
/// collecting all of its output.
|
||||
///
|
||||
@ -233,6 +252,7 @@ impl CommandExt for process::Command {
|
||||
Ok(child)
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
fn status_async(&mut self, handle: &Handle) -> StatusAsync {
|
||||
let mut inner = self.spawn_async(handle);
|
||||
if let Ok(child) = inner.as_mut() {
|
||||
@ -249,6 +269,21 @@ impl CommandExt for process::Command {
|
||||
}
|
||||
}
|
||||
|
||||
fn status_async2(&mut self, handle: &Handle) -> io::Result<StatusAsync2> {
|
||||
self.spawn_async(handle).map(|mut child| {
|
||||
// Ensure we close any stdio handles so we can't deadlock
|
||||
// waiting on the child which may be waiting to read/write
|
||||
// to a pipe we're holding.
|
||||
child.stdin.take();
|
||||
child.stdout.take();
|
||||
child.stderr.take();
|
||||
|
||||
StatusAsync2 {
|
||||
inner: child,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn output_async(&mut self, handle: &Handle) -> OutputAsync {
|
||||
self.stdout(Stdio::piped());
|
||||
self.stderr(Stdio::piped());
|
||||
@ -411,11 +446,14 @@ impl Future for WaitWithOutput {
|
||||
/// exit status. This future will resolves to the `ExitStatus` type in the
|
||||
/// standard library.
|
||||
#[must_use = "futures do nothing unless polled"]
|
||||
#[deprecated(note = "use the more flexible `SpawnAsync2` adapter instead")]
|
||||
#[allow(deprecated)]
|
||||
#[derive(Debug)]
|
||||
pub struct StatusAsync {
|
||||
inner: Flatten<FutureResult<Child, io::Error>>,
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl Future for StatusAsync {
|
||||
type Item = ExitStatus;
|
||||
type Error = io::Error;
|
||||
@ -425,6 +463,26 @@ impl Future for StatusAsync {
|
||||
}
|
||||
}
|
||||
|
||||
/// Future returned by the `CommandExt::status_async2` method.
|
||||
///
|
||||
/// This future is used to conveniently spawn a child and simply wait for its
|
||||
/// exit status. This future will resolves to the `ExitStatus` type in the
|
||||
/// standard library.
|
||||
#[must_use = "futures do nothing unless polled"]
|
||||
#[derive(Debug)]
|
||||
pub struct StatusAsync2 {
|
||||
inner: Child,
|
||||
}
|
||||
|
||||
impl Future for StatusAsync2 {
|
||||
type Item = ExitStatus;
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<ExitStatus, io::Error> {
|
||||
self.inner.poll()
|
||||
}
|
||||
}
|
||||
|
||||
/// Future returned by the `CommandExt::output_async` method.
|
||||
///
|
||||
/// This future is mostly equivalent to spawning a process and then calling
|
||||
|
@ -119,6 +119,7 @@ fn wait_with_output_captures() {
|
||||
assert_eq!(output.stderr.len(), 0);
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
#[test]
|
||||
fn status_closes_any_pipes() {
|
||||
let mut core = Core::new().unwrap();
|
||||
@ -136,3 +137,21 @@ fn status_closes_any_pipes() {
|
||||
Err(_) => panic!("failed to run futures"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn status2_closes_any_pipes() {
|
||||
let mut core = Core::new().unwrap();
|
||||
|
||||
// Cat will open a pipe between the parent and child.
|
||||
// If `status_async2` doesn't ensure the handles are closed,
|
||||
// we would end up blocking forever (and time out).
|
||||
let child = cat().status_async2(&core.handle()).unwrap();
|
||||
let timeout = Timeout::new(Duration::from_secs(1), &core.handle())
|
||||
.expect("timeout registration failed")
|
||||
.map(|()| panic!("time out exceeded! did we get stuck waiting on the child?"));
|
||||
|
||||
match core.run(child.select(timeout)) {
|
||||
Ok((status, _)) => assert!(status.success()),
|
||||
Err(_) => panic!("failed to run futures"),
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user