Rust drop before divergent function call

I'm writing my own implementation of threads (for fun), and I've decided not to use pthreads. I've been using the libc::clone wrapper function, but I was concerned about the fact that it doesn't allow one to specify the stack size.

Thankfully, there's the clone3 syscall, which provides the API I want (and, as a fun bonus, is even more low-level). However, it doesn't work quite the same: instead of supplying a function pointer for it to call, it forks the process much like fork, resulting in two processes continuing after the syscall returns.

My question is this: if I call a function that returns ! (and doesn't unwind, e.g. std::process::exit), can I rely on no drops being inserted before it? Otherwise, I'll get trivial and unavoidable double-drops from unrelated variables getting dropped in both threads.

For example, is the following sound?

fn main() {
    let foo = String::from("foo");
    let result = clone3(...); // Thin wrapper around the syscall
    
    // `result = 0` iff this is the child
    if result == 0 {
        // Run some fancy thread code

        // Can I rely on foo getting "forgotten" here? Can I rely on *all*
        // prior state (that's not explicitly dropped) getting forgotten?
        // Or could something potentially get dropped?
        std::process::exit(0);
    }

    drop(foo);
}

I'd also be curious what stability guarantees there are, whatever the semantics that are currently applied.

Directly from std::process::exit doc:

Note that because this function never returns, and that it terminates the process, no destructors on the current stack or any other thread’s stack will be run.

AFAIK relocating a drop to before a diverging call would violate the as-if principle and be considered a bug.


That said, your wrapper needs to be unsafe. Note how this compiles for example.

    if result == 0 {
        drop(foo);
        std::process::exit(0);
    }

    drop(foo);

(Compiles because the second drop is unreachable from the first.)

Yes, but that doesn't mean NLL couldn't (theoretically) relocate a drop... I don't think. In any case, thanks!

Absolutely, I omitted the unsafe for brevity. There are many ways an incorrect call to clone3 could break things.

Well, NLL doesn't, but I know what you mean.

Some people want borrow/liveness-driven (non-lexical) drop, but it would be a breaking change due to topics like this: unsafe code can depend on the currently well-defined potential drop sites.[1] It would also mean the borrow checker effects semantics, versus being a proofreader.


  1. lexical, overwrites, unwind... ↩︎

3 Likes