Pre-RFC: Should std::panic::set_hook have a per-thread version?

I have a crate that wraps some sensitive code in std::panic::catch_unwind and then on panic turns that into a normal error that’s usable by the calling program. This works very well and I wanted to go one step further and silence the panic completely to not even throw up stuff on stderr. But as it turns out I can’t do that without affecting global state. If the calling program uses threads I would be silencing panics throughout the app and that’s not a reasonable thing to do in a crate.

Here’s a simple example of that happening:

use std::panic;
use std::thread;
use std::time;

fn main() {
    let child = thread::spawn(|| {
        panic::set_hook(Box::new(|_info| {
            // do nothing to silence panic
        }));
        
        thread::sleep(time::Duration::from_secs(5));
    
        // We've set a hook so this panic should not write anything to stderr
        match panic::catch_unwind(|| panic!("test")) {
            Ok(_) => println!("No panic"),
            Err(_) => println!("Caught thread panic"),
        };
    });
    
    thread::sleep(time::Duration::from_secs(1));
    
    // Here we haven't set a hook so panic should write to stderr but doesn't
    // because the hook is global
    match panic::catch_unwind(|| panic!("test")) {
        Ok(_) => println!("No panic"),
        Err(_) => println!("Caught main panic"),
    };
    
    child.join().unwrap();
}

Here it is for silencing panic but if you just want extra stuff to be printed on panic the same applies, it’s not possible to do it without affecting the whole program. Ideally what I would want instead would be something like:

  • std::panic::set_thread_hook: this sets a hook that’s only applicable to the current thread and if set overrides the global hook. It would be nice if you could also set a flag to say that threads that are spawned from this one copy the same hook.
  • std::panic::take_thread_hook: this removes the per-thread hook from the current thread. Other threads that have been spawned since still have the inherited hook and extra calls would need to be done there

Does this make sense? Is it worth it to write a proper RFC?

This can be implemented pretty easily in a third party crate - there’s no need to bake it into the standard library.

1 Like

I don’t think you can do it robustly in a crate. It’s easy to write a crate that calls std::panic::set_hook and sets a hook that implements these behaviors. But then that crate will break if some other crate sets a hook as well.

This would have been useful in proc-macro2. We have a sufficient workaround for now but there is still a race condition we don’t know how to resolve using just panic::set_hook.


This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.