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

Tracking Issue for std::process error handling #73131

Open
2 of 6 tasks
ijackson opened this issue Jun 8, 2020 · 14 comments
Open
2 of 6 tasks

Tracking Issue for std::process error handling #73131

ijackson opened this issue Jun 8, 2020 · 14 comments
Labels
A-process Area: `std::process` and `std::env` C-tracking-issue Category: A tracking issue for an RFC or an unstable feature. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.

Comments

@ijackson
Copy link
Contributor

ijackson commented Jun 8, 2020

Hi. This is a tracking issue for various error handling awkwardnesses in std::process. It seemed useful to gather them together here. I hope you consider this helpful. I don't think this needs an RFC but I can make an RFC if people feel that would be best.

About tracking issues

Tracking issues are used to record the overall progress of implementation.
They are also uses as hubs connecting to other relevant issues, e.g., bugs or open design questions.
A tracking issue is however not meant for large scale discussion, questions, or bug reports about a feature.
Instead, open a dedicated issue for the specific matter and add the relevant feature gate label.

Relevant RFCs and proposals:

Platform-independent issues

Unix issues relating to ExitStatusExt

@ijackson ijackson added the C-tracking-issue Category: A tracking issue for an RFC or an unstable feature. label Jun 8, 2020
@ijackson
Copy link
Contributor Author

ijackson commented Dec 5, 2020

@rustbot modify labels +T-libs

@rustbot rustbot added the T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. label Dec 5, 2020
@KodrAus
Copy link
Contributor

KodrAus commented Jan 30, 2021

@ijackson Are you happy if we collapse those child issues into this one and consider everything we want to do to improve our std::process error handling together?

@KodrAus
Copy link
Contributor

KodrAus commented Jan 30, 2021

I was thinking our path could look something like:

  1. Design our API for converting ExitStatus into a Result.
  2. Update our docs (in particular our docs around ExitStatus are pretty sparse).
  3. Get the ExitStatus into Result API stabilized and update our examples to use it instead of ignoring them.
  4. Consider #[must_use] on ExitStatus.

What do you think?

@KodrAus
Copy link
Contributor

KodrAus commented Jan 30, 2021

On unix, failure to use a Child leaks the process. It continues running in parallel, even after we have exited (or crashed), possibly interfering with the user's terminal or driveling nonsense into a logfile long after the original event.

In #81452, am I reading that right and you're saying it's possible to spawn a child that outlives its parent process entirely?

@KodrAus
Copy link
Contributor

KodrAus commented Jan 30, 2021

As for what to make the API for ExitStatus into Result look like, I arrived at success_or_else after thinking:

  • We should try give our methods that convert a non Option/Result/Poll-like type into one a more semantic name, so that you can identify it in a chain of combinators.
  • We don't necessarily need to introduce a new ExitStatusError type that will look roughly the same as ExitStatus, except implement the Error trait, because ExitStatus is Copy, if you want to add it as context you can freely refer to it anywhere.

We could consider returning a io::Result<()> though, and passing a private inner error type instead that includes the exit code in its Display message.

I think it would be nice to try come up with a name that's a little clearer than either ok or into_result. In your example:

Command::new(adb_path)
      .arg("push")
      .arg(&exe_file)
      .arg(&self.config.adb_test_dir)
      .status()
      .unwrap_or_else(|_| panic!("failed to exec `{:?}`", adb_path));
      .ok()
      .unwrap_or_else(|s| panic!("`{:?}` failed: {}", adb_path, s));

I don't find it clear that .ok() is operating on a ExitStatus, I'm expecting it to be on a Result, making the last line operate on an Option, except that it's taking an argument into the closure, which Option doesn't do.

@ijackson
Copy link
Contributor Author

ijackson commented Jan 30, 2021 via email

@ijackson
Copy link
Contributor Author

I guess in summary, I think next step in this area should probably be to work up an MR for ExitStatusError. I think once you see it you'll like it :-).

@KodrAus
Copy link
Contributor

KodrAus commented Jan 31, 2021

We don't necessarily need to introduce a new
ExitStatusError type that will look roughly the same as
ExitStatus, except implement the Error trait, because ExitStatus
is Copy, if you want to add it as context you can freely refer
to it anywhere.

I don't follow this. You're saying that the Error value from
.exit_ok() (or whatever we call it) ought not to contain the exit
status ? Or that it ought to be something much more complicated ?

Ah, what I meant here was that I don't think we should introduce a dedicated ExitStatusError type. I think our options would be:

fn make_exit_status_a_result<E: Error>(self, err: E) -> Result<(), E>

or:

fn make_exit_status_a_result(self) -> io::Result<()>

Every other Result return in std::process uses io::Result, so I think we'd be best to use that here too. I don't believe a public dedicated error type is worth introducing, especially if it's exactly just ExitStatus, but implements the Error trait. If you want to get an ExitStatus to work with then you can inspect it directly from .status() and produce your own error type. It would be especially odd to introduce an Error type that can be converted back into an ExitStatus if we intend to mark it as #[must_use], the warning suggestion only really applies to the output of .status(), not to other accidentally unused ExitStatuses that should fall under the unused_variables lint.

@KodrAus
Copy link
Contributor

KodrAus commented Jan 31, 2021

Yes. That is the default behaviour of fork() if you exit without
waiting for the child. This is rarely desirable.

Yeh I can't think of a case where that's ever what I've wanted! We've previously rejected attempts to add #[must_use] to thread::spawn, because explicitly not joining a thread is a legitimate pattern and when your process goes away so do all those threads anyways.

Sorry for the mix-around, would you be happy to resubmit a PR that adds #[must_use] to Child and we can see what the rest of Libs think of it? It does seem justifiably different to thread::spawn, and the docs are already full of warnings.

@ijackson
Copy link
Contributor Author

Sorry for the mix-around, would you be happy to resubmit a PR that adds #[must_use] to Child and we can see what the rest of Libs think of it? It does seem justifiably different to thread::spawn, and the docs are already full of warnings.

Most of the examples in the stdlib leak the Child (!) and would need fixing. Doing that one #[must_use] now, and the rest later, would mean half-fixing the examples now and then fixing them properly later.

@ijackson
Copy link
Contributor Author

FYI the behaviour if you leak the child:

rustcargo@zealot:~$ cat t.rs
use std::process::Command;
fn main() {
    println!("Hello, world!");
    let mut cmd = Command::new("sh");
    cmd.args(&["-xec","
        echo hi
        sleep 5
        echo ho
    "]);
    let child = cmd.spawn().expect("spawn");
    dbg!(&child);
}
rustcargo@zealot:~$ rustc t.rs
rustcargo@zealot:~$ ./t
Hello, world!
[t.rs:11] &child = Child {
    stdin: None,
    stdout: None,
    stderr: None,
}
rustcargo@zealot:~$ + echo hi
hi
+ sleep 5
+ echo ho
ho

@ijackson
Copy link
Contributor Author

Ah, what I meant here was that I don't think we should introduce a dedicated ExitStatusError type.

Thanks for your comments. I still think, especially after the discussion on internals, that the dedicated ExitStatusError type is the right approach. I think I ought to be able to convince you but I don't want to annoy you and a bullet-point to-and-fro seems a poor approach. Also this tracking bug is not the ideal place to have this rather more specific conversation about this one question.

How about I write up my proposal in a mini-RFC style ("motivation", "alternatives" etc.) in #/73125 ?

@KodrAus
Copy link
Contributor

KodrAus commented Feb 1, 2021

@ijackson No problem! I'll also go and read through the pre-RFC discussion so I'm properly informed too.

@ijackson
Copy link
Contributor Author

ijackson commented Jan 3, 2023

Just added a link to rust-lang/rfcs#3362

@workingjubilee workingjubilee added the A-process Area: `std::process` and `std::env` label Jul 22, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-process Area: `std::process` and `std::env` C-tracking-issue Category: A tracking issue for an RFC or an unstable feature. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

4 participants