`Command::exec_replace`

On Unix, Rust has Command::exec to replace the running process. Windows doesn't have a direct equivalent but it can be emulated by waiting on the child process and then exiting.

Cargo's exec_replace abstracts over this difference. This a common pattern for unixy software so I feel it would be good to upstream something like this. Thoughts?

Note that Windows does provide the execvpe family of functions as part of the CRT, though they aren't available for use on UWP.

Yep, the idea would be to provide the same emulation as the libc function.

Although, testing it now, they seem to do the "dumb" thing and just exits immediately after spawning a process so it's not really process replacement. Tracing the system calls shows it does CreateProcessW(...) followed by ExitProcess(0).

The inconsistent state issue with a failing exec is I think a wrinkle in doing an exec_replace cross platform shim. Also, I think we'd prefer to return io::Result<!> instead of io::Error, limiting how quickly this could see stable turnaround.

duct is a fairly well-regarded library for doing process spawning the (opinionated) "right" way; working with them to provide a reasonably portable exec_replace behavior would serve as good precedent (in addition to cargo's emulation) for what std could do.

1 Like

What do people use exec-replace for anyway? I got the impression spawn was a much nicer low-level API from the start.

1 Like

Rustup calls exec in its shims to replace itself with the desired toolchain command, and then the parent process still gets the appropriate child signals and exit status. Otherwise, the shim would have to stick around and relay those, and not orphan the process doing the real work.

2 Likes

Thinking about use cases, I guess the main ones I see atm are:

  • Freeing the resources of the current process after spawning a new process
  • A parent waiting on a child but the child is a proxy that wants to defer to another process

The first one is trivially handled by Command::spawn followed by process::exit (or better yet, just exiting normally).

The second is probably ideally handled by Job Objects. But that's not really something you'd want to do behind the user's back even if you can. Alternatively the proxy could negotiate with the parent process to use a different process handle, etc. But that requires a lot of cooperation so it's not practical unless you control both sides.

Another way to handle it is for the proxy to wait on its own child and then exit. This has the advantage of working in most situations but the proxy will never be entirely transparent. And the proxy's resources stick around. Though honestly a proxy shouldn't be using up a lot of resources, especially when idly waiting. If it is a concern then perhaps that could also be mitigated by tearing down as much as possible before waiting on the child but that's not without issue either.

Ok, I'm starting to convince myself that there are a lot of situation specific nuances that may make this inappropriate for std. Or at least that any function(s) should be more focused on serving specific needs rather than just going further down the "emulating this Unix thing on Windows" rabbit hole.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.