Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Demonstrate best practice for feeding stdin of a child processes #82943

Merged
merged 1 commit into from
Mar 14, 2021

Conversation

kornelski
Copy link
Contributor

@kornelski kornelski commented Mar 9, 2021

Documentation change.

It's possible to create a deadlock with stdin/stdout I/O on a single thread:

  • the child process may fill its stdout buffer, and have to wait for the parent process to read it,
  • but the parent process may be waiting until its stdin write finishes before reading the stdout.

Therefore, the parent process should use separate threads for writing and reading.

These examples are not deadlocking in practice, because they use short strings, but I think it's better to demonstrate code that works even for long writes. The problem is non-obvious and tricky to debug (it seems that even libstd has a similar issue: #45572).

This also demonstrates how to use stdio with threads: it's not obvious that .take() can be used to avoid fighting with the borrow checker.

I've checked that the modified examples run fine.

@rust-highfive
Copy link
Collaborator

r? @joshtriplett

(rust-highfive has picked a reviewer for you, use r? to override)

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Mar 9, 2021
@joshtriplett
Copy link
Member

@kornelski I think explaining this issue and providing an example is a good idea. So is demonstrating take.

Would you consider adding a note explaining when this is needed, along the lines of: "This is an issue when running any program that doesn't have a stated guarantee that it reads its entire stdin before writing more than a pipe buffer's worth of output, if you want to feed it more than a pipe buffer's worth of input. The size of a pipe buffer varies on different targets."

Also, one other issue (which may or may not be possible to simply address here): it'd be nice if somewhere we had a good example of error handling for threads using ?. We still have many examples that use .expect(), dating back to when we couldn't easily use ? in doctests. But one issue with expect and threaded code is that it's easy to translate expect to a ? outside of a thread, but in a thread it isn't obvious how to handle errors. This isn't necessarily the example to fix that, though, if doing so isn't trivial.

@lukaslueg
Copy link
Contributor

This PR addresses a very common problem where people deadlock their program and can't figure out why. Without derailing this specific PR: Should we have a Process::communicate() like Python has for this exact reason?

It's possible to create a deadlock with stdin/stdout I/O on a single thread:

* the child process may fill its stdout buffer, and have to wait for the parent process to read it,
* but the parent process may be waiting until its stdin write finishes before reading the stdout.

Therefore, the parent process should use separate threads for writing and reading.
@kornelski
Copy link
Contributor Author

I've added more explanations.

I agree that changing unwrap() would be good. I can make another PR for this.

@joshtriplett
Copy link
Member

@bors r+ rollup

@bors
Copy link
Contributor

bors commented Mar 13, 2021

📌 Commit ce2d95c has been approved by joshtriplett

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Mar 13, 2021
@joshtriplett
Copy link
Member

@kornelski wrote:

I've added more explanations.

Looks great. Merging.

I agree that changing unwrap() would be good. I can make another PR for this.

Thank you!

bors added a commit to rust-lang-ci/rust that referenced this pull request Mar 14, 2021
Rollup of 10 pull requests

Successful merges:

 - rust-lang#81465 (Add documentation about formatting `Duration` values)
 - rust-lang#82121 (Implement Extend and FromIterator for OsString)
 - rust-lang#82617 (Document `everybody_loops`)
 - rust-lang#82789 (Get with field index from pattern slice instead of directly indexing)
 - rust-lang#82798 (Rename `rustdoc` to `rustdoc::all`)
 - rust-lang#82804 (std: Fix a bug on the wasm32-wasi target opening files)
 - rust-lang#82943 (Demonstrate best practice for feeding stdin of a child processes)
 - rust-lang#83066 (Add `reverse` search alias for Iterator::rev())
 - rust-lang#83070 (Update cargo)
 - rust-lang#83081 (Fix panic message of `assert_failed_inner`)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
@bors bors merged commit dda9d05 into rust-lang:master Mar 14, 2021
@rustbot rustbot added this to the 1.52.0 milestone Mar 14, 2021
@kornelski kornelski deleted the threadstdio branch March 14, 2021 11:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants