If you wish to have a spawned thread abort on panic, all you have to do is add some abort on unwind bomb:
pub fn spawn_aborting_if_unwind<T, F> (f: F) -> JoinHandle<T>
where
F : FnOnce() -> T,
F : Send + 'static,
T : Send + 'static,
{
::std::thread::spawn(|| {
::scopeguard::defer_on_unwind!({ ::std::process::abort() });
f()
})
}
Version without scopeguard
pub fn spawn_aborting_if_unwind<T, F> (f: F) -> JoinHandle<T>
where
F : FnOnce() -> T,
F : Send + 'static,
T : Send + 'static,
{
struct AbortOnDrop();
impl Drop for AbortOnDrop {
fn drop (self: &mut Self)
{
::std::process::abort();
}
}
::std::thread::spawn(|| {
let abort_on_drop = AbortOnDrop();
let ret = f();
::core::mem::forget(abort_on_drop);
ret
})
}
But, again, this only truly works if the main thread waits for the spawned threads, so using a scoped API such as ::crossbeam
's, or one where the JoinHandle
s .join()
on drop would make this design quite good.
That being said, I do agree that making panics "cross" the thread boundary rather than not (here, for instance, by aborting the process) is a suprising default w.r.t. other guarantees.
While the fact that you can run a thread and know that if it panics the failure can be contained there is indeed quite useful and definitely something that was worth designing around, this "capability" should have been opt-in, rather than opt-out as showcased at the beginning of my post.
Something that "proves" my point is the fact that spawning a thread does not require them to be UnwindSafe
& co.
A nicer design would have been for thread::spawn
to spawn a thread where panics abort (at the end of the unwind chain), and with two opt-in settings on that JoinHandle
:
-
.ignore_unwind()
, that would switch to the currently default behavior, while requiring the provided F
closure parameter be UnwindSafe
!
thread::spawn(|| { ... })
.ignoring_unwind() // there is a race condition with this pattern, but it shouldn't matter
// alternative non-racy version (with a type-level enum, we can still require UnwindSafe when applicable)
thread::spawn(|| { ... }, OnUnwind::Ignore)
-
.join_on_drop()
, that would enable joining the thread when the JoinHandle
would be dropped, thus making it possible to propagate the panic without aborting, by making the .join()
call panic itself (infecting the JoinHandle
with the UnwindSafe
ty of F
).