Does std::panic::catch_unwind spawn a new thread?

I’ve been looking for a while but the docs leave it unclear whether this fn spawns a new thread or not. Does it?

1 Like

No, it does not. Things in std don’t in general start threads on their own unless they say so. There’s no reason to say it doesn’t.

2 Likes

Making things clearer wouldn’t hurt. Stack unwinding stops at thread boundaries or at a panic::catch_unwind. Without further knowledge of the black box that unwinding is (for most people), guessing that catch_unwind may be implemented by spawning a thread is a plausible guess: not everybody knows of Rust “transparency” runtime-wise.

So I think that a short sentence clarifying this point could definitely be added :wink:

5 Likes
pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R>

Neither F nor R require the Send trait, so it would generally be illegal to send them across threads.

It’s possible that it could internally use specialization for F: Send and R: Send, and only use threads in that case. But since it has to handle the !Send case already, why would it bother specializing? So it’s pretty safe to assume that it will run directly, from the type signature alone.

4 Likes

FWIW, stopping at the thread boundary is also due to panic::catch_unwind:

3 Likes

Touché

Actually, I didn’t think that the thread spawning takes place, just saw some mentioning about ‘thread boundary’ somewhere in std::panic, got curious, was too lazy to check myself :slight_smile:

It does make sense, but it’s not obvious.

There are abstractions like scoped threads which allow passing of references across threads. It wouldn’t be unreasonable to (technically incorrectly) assume that such mechanism may do something clever with an internal thread and blocking of the caller to make it safe.

1 Like

Sure, scoped threads take care of 'static, and I suppose blocking the caller would avoid basic data races, but there’s other state that might tie a value to a thread too. Suppose you have an index associated with some TLS cache, or a main-thread-only GUI handle, or any kind of unsafe user code where the thread context is one of its invariants. So I think it’s not a matter of being clever enough – Rust’s unwinding runtime would be unsound to cheat here.

I don’t mean to say that anyone is wrong to wonder about this, but I think the answer is a firm no, it cannot.

The fact that catch_unwind returns a thread::Result might also be a slight source of confusion about whether or not it spawns a thread.

5 Likes

I still could see where this comes from and wouldn’t have been able to say by myself. Before catch_unwind the only option to catch panics was creating a new thread which you monitor, so it would have made total sense.