Skip to content

Commit 62efb0e

Browse files
authored
Merge pull request #91 from epage/deadlock
fix(cmd): Don't deadlock between pipes
2 parents f2c7729 + 482881f commit 62efb0e

File tree

1 file changed

+44
-12
lines changed

1 file changed

+44
-12
lines changed

src/cmd.rs

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
55
use std::ffi;
66
use std::io;
7-
use std::io::Write;
7+
use std::io::{Read, Write};
88
use std::path;
99
use std::process;
1010

@@ -416,7 +416,48 @@ impl Command {
416416
/// assert!(output.status.success());
417417
/// ```
418418
pub fn output(&mut self) -> io::Result<process::Output> {
419-
self.spawn()?.wait_with_output()
419+
let spawn = self.spawn()?;
420+
Self::wait_with_input_output(spawn, self.stdin.clone())
421+
}
422+
423+
/// If `input`, write it to `child`'s stdin while also reading `child`'s
424+
/// stdout and stderr, then wait on `child` and return its status and output.
425+
///
426+
/// This was lifted from `std::process::Child::wait_with_output` and modified
427+
/// to also write to stdin.
428+
fn wait_with_input_output(
429+
mut child: process::Child,
430+
input: Option<Vec<u8>>,
431+
) -> io::Result<process::Output> {
432+
let stdin = input.and_then(|i| {
433+
child
434+
.stdin
435+
.take()
436+
.map(|mut stdin| std::thread::spawn(move || stdin.write_all(&i)))
437+
});
438+
fn read<R>(mut input: R) -> std::thread::JoinHandle<io::Result<Vec<u8>>>
439+
where
440+
R: Read + Send + 'static,
441+
{
442+
std::thread::spawn(move || {
443+
let mut ret = Vec::new();
444+
input.read_to_end(&mut ret).map(|_| ret)
445+
})
446+
}
447+
let stdout = child.stdout.take().map(read);
448+
let stderr = child.stderr.take().map(read);
449+
450+
// Finish writing stdin before waiting, because waiting drops stdin.
451+
stdin.and_then(|t| t.join().unwrap().ok());
452+
let status = child.wait()?;
453+
let stdout = stdout.and_then(|t| t.join().unwrap().ok());
454+
let stderr = stderr.and_then(|t| t.join().unwrap().ok());
455+
456+
Ok(process::Output {
457+
status: status,
458+
stdout: stdout.unwrap_or_default(),
459+
stderr: stderr.unwrap_or_default(),
460+
})
420461
}
421462

422463
fn spawn(&mut self) -> io::Result<process::Child> {
@@ -425,16 +466,7 @@ impl Command {
425466
self.cmd.stdout(process::Stdio::piped());
426467
self.cmd.stderr(process::Stdio::piped());
427468

428-
let mut spawned = self.cmd.spawn()?;
429-
430-
if let Some(buffer) = self.stdin.as_ref() {
431-
spawned
432-
.stdin
433-
.as_mut()
434-
.expect("Couldn't get mut ref to command stdin")
435-
.write_all(&buffer)?;
436-
}
437-
Ok(spawned)
469+
self.cmd.spawn()
438470
}
439471
}
440472

0 commit comments

Comments
 (0)