Mercurial > forks > helix
changeset 6501:4e371d250f73
Join input and wait tasks in external formatter Tokio command
This matches the layout of `shell_impl_async` in `commands.rs` and
avoids a hang or maybe deadlock in `to_writer`'s calls to
`tokio::io::AsyncWriteExt::write_all`. I don't really understand the
underlying cause of the hang but it seems it's necessary to spawn a
new tokio task to provide input to stdin. This is shown in an example
in `tokio::process::Child::wait` but not documented explicitly.
author | Michael Davis <mcarsondavis@gmail.com> |
---|---|
date | Sat, 01 Feb 2025 10:31:42 -0500 |
parents | 9be91b29148d |
children | 52004cac7fd4 |
files | helix-view/src/document.rs |
diffstat | 1 files changed, 14 insertions(+), 10 deletions(-) [+] |
line wrap: on
line diff
--- a/helix-view/src/document.rs Sat Feb 01 09:10:04 2025 -0500 +++ b/helix-view/src/document.rs Sat Feb 01 10:31:42 2025 -0500 @@ -796,17 +796,21 @@ command: fmt_cmd.to_string_lossy().into(), error: e.kind(), })?; - { - let mut stdin = process.stdin.take().ok_or(FormatterError::BrokenStdin)?; - to_writer(&mut stdin, (encoding::UTF_8, false), &text) - .await - .map_err(|_| FormatterError::BrokenStdin)?; - } - let output = process - .wait_with_output() - .await - .map_err(|_| FormatterError::WaitForOutputFailed)?; + let mut stdin = process.stdin.take().ok_or(FormatterError::BrokenStdin)?; + let input_text = text.clone(); + let input_task = tokio::spawn(async move { + to_writer(&mut stdin, (encoding::UTF_8, false), &input_text).await + // Note that `stdin` is dropped here, causing the pipe to close. This can + // avoid a deadlock with `wait_with_output` below if the process is waiting on + // stdin to close before exiting. + }); + let (input_result, output_result) = tokio::join! { + input_task, + process.wait_with_output(), + }; + let _ = input_result.map_err(|_| FormatterError::BrokenStdin)?; + let output = output_result.map_err(|_| FormatterError::WaitForOutputFailed)?; if !output.status.success() { if !output.stderr.is_empty() {