Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I cause a panic on a thread to immediately end the main thread?

Tags:

rust

In Rust, a panic terminates the current thread but is not sent back to the main thread. The solution we are told is to use join. However, this blocks the currently executing thread. So if my main thread spawns 2 threads, I cannot join both of them and immediately get a panic back.

let jh1 = thread::spawn(|| { println!("thread 1"); sleep(1000000); };
let jh2 = thread::spawn(|| { panic!("thread 2") };

In the above, if I join on thread 1 and then on thread 2 I will be waiting for 1 before ever receiving a panic from either thread

Although in some cases I desire the current behavior, my goal is to default to Go's behavior where I can spawn a thread and have it panic on that thread and then immediately end the main thread. (The Go specification also documents a protect function, so it is easy to achieve Rust behavior in Go).

like image 749
Greg Weber Avatar asked Mar 14 '16 13:03

Greg Weber


People also ask

What is thread panic?

In Rust, a panic terminates the current thread but is not sent back to the main thread. The solution we are told is to use join . However, this blocks the currently executing thread. So if my main thread spawns 2 threads, I cannot join both of them and immediately get a panic back.

When to panic Rust?

Similarly, panic! is often appropriate if you're calling external code that is out of your control and it returns an invalid state that you have no way of fixing. However, when failure is expected, it's more appropriate to return a Result than to make a panic!


1 Answers

Updated for Rust 1.10+, see revision history for the previous version of the answer

good point, in go the main thread doesn't get unwound, the program just crashes, but the original panic is reported. This is in fact the behavior I want (although ideally resources would get cleaned up properly everywhere).

This you can achieve with the recently stable std::panic::set_hook() function. With it, you can set a hook which prints the panic info and then exits the whole process, something like this:

use std::thread;
use std::panic;
use std::process;

fn main() {
    // take_hook() returns the default hook in case when a custom one is not set
    let orig_hook = panic::take_hook();
    panic::set_hook(Box::new(move |panic_info| {
        // invoke the default handler and exit the process
        orig_hook(panic_info);
        process::exit(1);
    }));

    thread::spawn(move || {
        panic!("something bad happened");
    }).join();

    // this line won't ever be invoked because of process::exit()
    println!("Won't be printed");
}

Try commenting the set_hook() call out, and you'll see that the println!() line gets executed.

However, this approach, due to the use of process::exit(), will not allow resources allocated by other threads to be freed. In fact, I'm not sure that Go runtime allows this as well; it is likely that it uses the same approach with aborting the process.

like image 89
Vladimir Matveev Avatar answered Oct 09 '22 12:10

Vladimir Matveev