Would it be possible for std::process::Command to be Send?

I'd like to send a process:Command to a new thread, but that does not work, because on Unix, the Command struct has a raw pointer field:

pub struct Command {
    // Currently we try hard to ensure that the call to `.exec()` doesn't
    // actually allocate any memory. While many platforms try to ensure that
    // memory allocation works after a fork in a multithreaded process, it's
    // been observed to be buggy and somewhat unreliable, so we do our best to
    // just not do it at all!
    //
    // Along those lines, the `argv` and `envp` raw pointers here are exactly
    // what's gonna get passed to `execvp`. The `argv` array starts with the
    // `program` and ends with a NULL, and the `envp` pointer, if present, is
    // also null-terminated.
    //
    // Right now we don't support removing arguments, so there's no much fancy
    // support there, but we support adding and removing environment variables,
    // so a side table is used to track where in the `envp` array each key is
    // located. Whenever we add a key we update it in place if it's already
    // present, and whenever we remove a key we update the locations of all
    // other keys.
    program: CString,
    args: Vec<CString>,
    argv: Vec<*const c_char>,
    env: CommandEnv<DefaultEnvKey>,

    cwd: Option<CString>,
    uid: Option<uid_t>,
    gid: Option<gid_t>,
    saw_nul: bool,
    closures: Vec<Box<FnMut() -> io::Result<()> + Send + Sync>>,
    stdin: Option<Stdio>,
    stdout: Option<Stdio>,
    stderr: Option<Stdio>,
}

Would it be possible to make argv a CString (or OsString, I'm not sure what the difference is between these two types) instead of a for Vec<*const c_char> ?

argv is defined that way because it's the type of array that libc::execvp needs. But it's pointing to the memory owned by args, so we could probably just manually implement Send and Sync anyway.

I think the problem here is that argv must be terminated by a null pointer.

Should I open an issue on rust-lang/rust?

Yeah but a CString is nul-terminated too.

Yeah, filing an issue would be good.

I think it will mostly be a question of whether the libs team wants to commit to Command being Send + Sync forever. But it effectively must be so anyway, since it is used across a fork.

The array itself must end with a null pointer, as that's the only way execvp knows how many args to read.

1 Like

The problem is not the raw strings, but that the list of pointers must have a 0 pointer at the end.

(see http://pages.cs.wisc.edu/~smoler/cs354/onyourown/C.argv.html, search for "ANSI standard")

I'd recommend going @cuviper's round and would open an issue asking for a Send bound.

1 Like

I ended up opening an issue and a PR to implement Send for Command. My first unsafe piece of code, and it's now part of the standard library! :tada:

Thanks a lot!

3 Likes