Skip to content

Commit d233912

Browse files
committed
Experiment with fixing wait_with_pipe() another way
1 parent e91da59 commit d233912

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
@@ -290,42 +290,42 @@ fn test_pipe() -> CmdResult {
290290
.wait_with_pipe(&mut |_stdout| {})
291291
.is_ok());
292292

293-
// wait_with_pipe_thread() checks the exit status of the last child, even if pipefail is disabled
293+
// wait_with_borrowed_pipe() checks the exit status of the last child, even if pipefail is disabled
294294
set_pipefail(false);
295295
assert!(spawn_with_output!(true | false)?
296-
.wait_with_pipe_thread(|_stdout| {})
296+
.wait_with_borrowed_pipe(&mut |_stdout| {})
297297
.is_err());
298298
assert!(spawn_with_output!(true | true)?
299-
.wait_with_pipe_thread(|_stdout| {})
299+
.wait_with_borrowed_pipe(&mut |_stdout| {})
300300
.is_ok());
301301
assert!(spawn_with_output!(false)?
302-
.wait_with_pipe_thread(|_stdout| {})
302+
.wait_with_borrowed_pipe(&mut |_stdout| {})
303303
.is_err());
304304
assert!(spawn_with_output!(true)?
305-
.wait_with_pipe_thread(|_stdout| {})
305+
.wait_with_borrowed_pipe(&mut |_stdout| {})
306306
.is_ok());
307307
set_pipefail(true);
308-
// wait_with_pipe_thread() checks the exit status of the other children, unless pipefail is disabled
308+
// wait_with_borrowed_pipe() checks the exit status of the other children, unless pipefail is disabled
309309
set_pipefail(false);
310310
assert!(spawn_with_output!(false | true)?
311-
.wait_with_pipe_thread(|_stdout| {})
311+
.wait_with_borrowed_pipe(&mut |_stdout| {})
312312
.is_ok());
313313
set_pipefail(true);
314314
assert!(spawn_with_output!(false | true)?
315-
.wait_with_pipe_thread(|_stdout| {})
315+
.wait_with_borrowed_pipe(&mut |_stdout| {})
316316
.is_err());
317317
assert!(spawn_with_output!(true | true)?
318-
.wait_with_pipe_thread(|_stdout| {})
318+
.wait_with_borrowed_pipe(&mut |_stdout| {})
319319
.is_ok());
320-
// wait_with_pipe_thread() handles `ignore`
320+
// wait_with_borrowed_pipe() handles `ignore`
321321
assert!(spawn_with_output!(ignore false | true)?
322-
.wait_with_pipe_thread(|_stdout| {})
322+
.wait_with_borrowed_pipe(&mut |_stdout| {})
323323
.is_ok());
324324
assert!(spawn_with_output!(ignore true | false)?
325-
.wait_with_pipe_thread(|_stdout| {})
325+
.wait_with_borrowed_pipe(&mut |_stdout| {})
326326
.is_ok());
327327
assert!(spawn_with_output!(ignore false)?
328-
.wait_with_pipe_thread(|_stdout| {})
328+
.wait_with_borrowed_pipe(&mut |_stdout| {})
329329
.is_ok());
330330

331331
Ok(())

0 commit comments

Comments
 (0)