Skip to content

Tracking Issue for panic::update_hook #92649

Open
@Badel2

Description

@Badel2

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

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?

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFCT-libs-apiRelevant to the library API team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions