Description
Feature gate: #![feature(panic_update_hook)]
This is a tracking issue for the panic::update_hook
function.
This function allows creating panic hooks that forward the call to the previously set panic hook, without race conditions. It works by taking a closure that receives the old panic hook and the panic info, and it is atomically set as the panic hook by ensuring that during the execution of panic::update_hook
no other thread can modify the panic hook.
This common use case:
let prev = panic::take_hook();
panic::set_hook(Box::new(move |info| {
println!("panic handler A");
prev(info);
}));
Becomes:
panic::update_hook(|prev, info| {
println!("panic handler A");
prev(info);
});
Using the old approach is fine if there is some guarantee that there are no other threads trying to change the panic hook. The main goal of this feature is to enable the use case of safely adding panic hooks after the application has been initialized, because the existing functions panic::set_hook
and panic::take_hook
are not enough to implement that, consider this case:
- Thread A calls
panic::take_hook()
- Thread B calls
panic::take_hook()
- Thread A calls
panic::set_hook()
- Thread B calls
panic::set_hook()
And the result is that the original panic hook has been lost, as well as the panic hook set by thread A. The resulting panic hook will be the one set by thread B, which forwards to the default panic hook. Using the new panic::update_hook
function, this race condition is impossible, and the result will be either A, B, original
or B, A, original
.
Public API
// std::panic
pub fn update_hook<F>(hook_fn: F)
where
F: Fn(&(dyn Fn(&PanicInfo<'_>) + Send + Sync + 'static), &PanicInfo<'_>)
+ Sync
+ Send
+ 'static,
Steps / History
- Implementation: Implement
panic::update_hook
#92598 - Final comment period (FCP)
- Stabilization PR
Unresolved Questions
- Is the API flexible enough for most use cases? For example it is not possible to drop the previous handler, but if the user wanted to replace the old handler they could just use
panic::set_hook
. An alternative implementation is to take a closure that creates a panic handler, like this (available in commit 8bdf5c3):
panic::update_hook(|prev| {
Box::new(move |info| {
println!("panic handler A");
prev(info);
})
});
That approach should be more flexible because it transfers ownership to the closure, but if the outermost closure panics the error message is "panicked while processing panic" which is not nice. This is a consequence of holding the HOOK_LOCK
while executing the closure. Could be avoided using catch_unwind?