Why does a main thread panic not exit the process when a scoped thread is running?

eg.

    std::thread::scope(|s| {
        let t = s.spawn(|| loop {
            println!("... {:?}", thread::current().id());
            thread::sleep(std::time::Duration::from_secs(1));
        });
        thread::sleep(std::time::Duration::from_secs(1));
        panic!("oh dear");
    });

With a non-scoped thread, the process would exit as expected, but here it doesn't. I'd be interested to know why, and what should be done to ensure exit if the main thread panics when using a scoped thread.

1 Like

The thread can't exit the scope block until all threads spawned in it complete, whether that's via a normal return or unwinding after a panic.

The point of scoped threads is to allow the threads spawned in the scope to borrow data from the current thread. That means as long as the scoped threads are running the scope function must not be exited, either by returning or unwinding. Otherwise the scoped threads could potentially access freed memory, among other things.

OK that makes sense. It seems a substantial departure from the docs' description of Rust's threading model:

When the main thread of a Rust program terminates, the entire program shuts down, even if other threads are still running. However, this module provides convenient facilities for automatically waiting for the termination of a thread (i.e., join).

Anyway that aside, other than ensuring main never panics, which I guess is ideal, what should be done to ensure a scoped thread exits when main does?

There is no special case here where the threading model is different; rather, the main thread hasn't exited yet. The unwind from panic is in progress but blocked on the scope ending.

I think a useful way to look at this is that the thread which starts the scope is temporarily participating in the scope; the scope() doesn't return until all scoped threads and the body of the closure all have finished.

If you want the main thread to be able to exit and terminate everything, then do not start a scope from the main thread.

5 Likes

Thanks. So perhaps not a departure from the model in a proper conceptual sense, but it's at least a little confusing from surface-level reading of the docs. Another example:

If the main thread panics it will terminate all your threads and end your program with code 101.

So did panic! in this case not actually panic the main thread?

Terminological quibbles aside I did come across this as a real issue. I guess I should just go back to using a non-scoped thread. I think I only used a scoped thread to avoid a couple of clones.

scope is using catch_unwind to pause the panic until the other threads exit. So the panic on the main thread hasn't reached the point where it would cause the process to exit yet.

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.