tokio::task::spawn (and its variants) avoid an
UnwindSafe bound on the supplied future? Is there some mechanism besides
std::panic::catch_unwind that the poller uses?
Just like how this is "incorrect" for
std::thread::spawn, it is also "incorrect" for
tokio::spawn to lack these. However, the traits are safe, so it's not a big deal.
Note that you don't need
UnwindSafe to actually catch the panics - unwind safety isn't about the ability to catch the panics, but about what you can safely assume about anything shared between the code that panicked and the code catching the panic.
The point of
UnwindSafe is to tell you that this type's invariants are never broken by a panic, and thus it's safe to inspect a value of that type after a panic has been caught. Because
tokio::task::spawn does not inspect the contents of the future after a panic has been caught, it doesn't need the future to be
UnwindSafe. It does need other things (such as the implementation details of
JoinHandle) to be
UnwindSafe, since it will touch them after a panic, but Tokio is written so that those things are
Couldn't you create a future that owns a
Rc<RefCell<T>>, spawn it locally, have it panic, and then later observe the
RefCell contents? Is
UnwindSafe just an advisory tag?
It's safe to implement and there's even a provided (and safe) way to ignore it, so yes. It's meant to serve as a lint for logic errors. If it's possible to create UB by ignoring it, that's still the fault of some
unsafe somewhere, and not of the programmer ignoring it.
You can, and as it happens, both
T is both
This is the only reason
UnwindSafe exists - it allows you to opt-in to error E0277 where you think that there's a strong chance that a parameter not being unwind safe is a logic error.
catch_unwind uses it, because it expects that anything modified in the closure you pass it will also be referenced after any unwind, and this should be caught.
Rc<S>: UnwindSafe require
S: RefUnwindSafe (which
RefCell<T> doesn't satisfy)?
What is different about
catch_unwind vs spawning a task or thread? Just that you need to opt out of unwind safety rather than in? Or is there something special about unwinding a thread?
Good point - I misread the docs for
So, the only way to not be unwind safe is to share something that is not unwind safe with your environment - everything that's internal to you is irrelevant (since it's all destroyed during unwinding anyway) - and the presumption is different between
catch_unwind, if the closure shares something with the environment outside
catch_unwind that's not unwind safe, it's quite likely that it does so because you're going to mutate it from inside the closure and then inspect it later - the authors of
catch_unwind want you to know that you're doing something that probably won't work the way you want it to.
spawn, the normal case is that you're not going to inspect anything that you've passed into the closure - chances are that if it is shared with the environment, it's either unwind safe (e.g.
Mutex<_>), or it's just an artefact of how closures capture things by default, and you're not going to look at it after handing it off. In this case, the E0277 error because your argument to
spawn is not unwind safe is almost always going to be a false positive, and rather than forcing you to use
AssertUnwindSafe all the time, it's easier to not have the bound to begin with.
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.