Skip to content

Commit 4e0e99e

Browse files
committed
Experiment with fixing wait_with_pipe() another way
1 parent cd87367 commit 4e0e99e

File tree

2 files changed

+57
-26
lines changed

2 files changed

+57
-26
lines changed

src/child.rs

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ impl FunChildren {
116116
}
117117

118118
/// Pipes stdout from the last child in the pipeline to the given function, which runs in
119-
/// **the current thread**, then waits for all of the children to exit.
119+
/// the current thread, then waits for all of the children to exit.
120120
///
121121
/// <div class=warning>
122122
///
@@ -153,19 +153,50 @@ impl FunChildren {
153153
}
154154

155155
/// Pipes stdout from the last child in the pipeline to the given function, which runs in
156-
/// **a new thread**, then waits for all of the children to exit.
157-
pub fn wait_with_pipe_thread(
158-
&mut self,
159-
f: impl FnOnce(Box<dyn Read + Send>) + Send + 'static,
160-
) -> CmdResult {
161-
if let Some(stdout) = self.children.last_mut().unwrap().stdout.take() {
162-
let thread = std::thread::spawn(|| f(Box::new(stdout)));
163-
let wait_res = self.wait_with_output().map(|_| ());
164-
thread.join().expect("stdout thread panicked");
165-
return wait_res;
166-
}
156+
/// the current thread, then waits for all of the children to exit.
157+
///
158+
/// If the function returns early, without reading from stdout until the last child exits,
159+
/// then the rest of stdout is automatically read and discarded to allow the child to finish.
160+
pub fn wait_with_borrowed_pipe(&mut self, f: &mut dyn FnMut(&mut Box<dyn Read>)) -> CmdResult {
161+
let mut last_child = self.children.pop().unwrap();
162+
let mut stderr_thread = StderrThread::new(
163+
&last_child.cmd,
164+
&last_child.file,
165+
last_child.line,
166+
last_child.stderr.take(),
167+
false,
168+
);
169+
let last_child_res = if let Some(stdout) = last_child.stdout {
170+
let mut stdout: Box<dyn Read> = Box::new(stdout);
171+
f(&mut stdout);
172+
let mut buf = vec![0; 65536];
173+
let status = loop {
174+
if let CmdChildHandle::Proc(child) = &mut last_child.handle {
175+
if let Ok(Some(status)) = child.try_wait() {
176+
break status;
177+
}
178+
}
179+
let _ = stdout.read(&mut buf);
180+
};
181+
if status.success() {
182+
Ok(())
183+
} else {
184+
Err(CmdChildHandle::status_to_io_error(
185+
status,
186+
&last_child.cmd,
187+
&last_child.file,
188+
last_child.line,
189+
))
190+
}
191+
} else {
192+
last_child.wait(true)
193+
};
194+
let other_children_res = CmdChildren::wait_children(&mut self.children);
195+
let _ = stderr_thread.join();
167196

168-
Ok(())
197+
self.ignore_error
198+
.then_some(Ok(()))
199+
.unwrap_or(last_child_res.and(other_children_res))
169200
}
170201

171202
/// Returns the OS-assigned process identifiers associated with these children processes.

tests/test_macros.rs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -177,42 +177,42 @@ fn test_pipe() -> CmdResult {
177177
.wait_with_pipe(&mut |_stdout| {})
178178
.is_ok());
179179

180-
// wait_with_pipe_thread() checks the exit status of the last child, even if pipefail is disabled
180+
// wait_with_borrowed_pipe() checks the exit status of the last child, even if pipefail is disabled
181181
set_pipefail(false);
182182
assert!(spawn_with_output!(true | false)?
183-
.wait_with_pipe_thread(|_stdout| {})
183+
.wait_with_borrowed_pipe(&mut |_stdout| {})
184184
.is_err());
185185
assert!(spawn_with_output!(true | true)?
186-
.wait_with_pipe_thread(|_stdout| {})
186+
.wait_with_borrowed_pipe(&mut |_stdout| {})
187187
.is_ok());
188188
assert!(spawn_with_output!(false)?
189-
.wait_with_pipe_thread(|_stdout| {})
189+
.wait_with_borrowed_pipe(&mut |_stdout| {})
190190
.is_err());
191191
assert!(spawn_with_output!(true)?
192-
.wait_with_pipe_thread(|_stdout| {})
192+
.wait_with_borrowed_pipe(&mut |_stdout| {})
193193
.is_ok());
194194
set_pipefail(true);
195-
// wait_with_pipe_thread() checks the exit status of the other children, unless pipefail is disabled
195+
// wait_with_borrowed_pipe() checks the exit status of the other children, unless pipefail is disabled
196196
set_pipefail(false);
197197
assert!(spawn_with_output!(false | true)?
198-
.wait_with_pipe_thread(|_stdout| {})
198+
.wait_with_borrowed_pipe(&mut |_stdout| {})
199199
.is_ok());
200200
set_pipefail(true);
201201
assert!(spawn_with_output!(false | true)?
202-
.wait_with_pipe_thread(|_stdout| {})
202+
.wait_with_borrowed_pipe(&mut |_stdout| {})
203203
.is_err());
204204
assert!(spawn_with_output!(true | true)?
205-
.wait_with_pipe_thread(|_stdout| {})
205+
.wait_with_borrowed_pipe(&mut |_stdout| {})
206206
.is_ok());
207-
// wait_with_pipe_thread() handles `ignore`
207+
// wait_with_borrowed_pipe() handles `ignore`
208208
assert!(spawn_with_output!(ignore false | true)?
209-
.wait_with_pipe_thread(|_stdout| {})
209+
.wait_with_borrowed_pipe(&mut |_stdout| {})
210210
.is_ok());
211211
assert!(spawn_with_output!(ignore true | false)?
212-
.wait_with_pipe_thread(|_stdout| {})
212+
.wait_with_borrowed_pipe(&mut |_stdout| {})
213213
.is_ok());
214214
assert!(spawn_with_output!(ignore false)?
215-
.wait_with_pipe_thread(|_stdout| {})
215+
.wait_with_borrowed_pipe(&mut |_stdout| {})
216216
.is_ok());
217217

218218
Ok(())

0 commit comments

Comments
 (0)