Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upCustom panic handlers in the standard library #30449
Comments
alexcrichton
added
A-libs
B-RFC-approved
labels
Dec 17, 2015
This comment has been minimized.
This comment has been minimized.
|
See rust-lang-nursery/log#67 for one usage example. |
This comment has been minimized.
This comment has been minimized.
yberreby
commented
Jan 12, 2016
|
Is there a good reason for the global panic handler to run even if the panic is #![feature(std_panic, recover)]
use std::panic;
fn main() {
panic::recover(|| {
panic!("hello");
}).unwrap_err();
}This prints |
This comment has been minimized.
This comment has been minimized.
|
There's some talk about this in the RFC PR, but the main reason is that it's very important for the handler to be able to take stack traces. In addition, the intended use cases for |
alexcrichton
added
T-libs
B-unstable
and removed
B-RFC-approved
labels
Jan 12, 2016
This comment has been minimized.
This comment has been minimized.
|
My biggest usecase for this is #1089, so I'd love a way to suppress the std panic handler, too. I would like to present my users with an error message, set an appropriate exit code, and be assured that desctructors ran without wrapping basically all my return values in Results when an error happens very rarely, is dependant on certain combinations of user input that can't be checked early/not very deep in the call chain. |
This comment has been minimized.
This comment has been minimized.
|
@shahn |
This comment has been minimized.
This comment has been minimized.
samphippen
commented
Jan 17, 2016
|
I'm trying to do something of the form: let orig_handler = panic::take_handler();
panic::set_handler(|_| ());
<an actual thing>
panic::set_handler(orig_handler);and getting the error
I tried taking ownership of the value inside the box with panic::set_handler(*orig_handler);but this gives the error
I feel like it should be easy to |
This comment has been minimized.
This comment has been minimized.
yberreby
commented
Jan 17, 2016
|
@samphippen This is ugly, but it works. #![feature(std_panic, panic_handler)]
use std::panic;
fn main() {
let orig_handler = panic::take_handler();
panic::set_handler(|_| ());
// <an actual thing>
panic::set_handler(move |info| (*orig_handler)(info));
} |
This comment has been minimized.
This comment has been minimized.
samphippen
commented
Jan 17, 2016
|
thanks! |
This comment has been minimized.
This comment has been minimized.
samphippen
commented
Jan 17, 2016
|
Further to this, I think there's a race or bug in the implementation. If this is the wrong place to report this issue please let me know, and I'll delete this comment and put it in the appropriate place. I'm aware this feature is unstable as well, but I figured I'd report some real world feedback anyway. My use case boils down to the following rust program: #![feature(panic_handler, recover, std_panic)]
use std::thread;
use std::panic::{self, recover};
fn main() {
let mut build = Vec::new();
for _ in 0..10 {
build.push(thread::spawn(|| {
let orig_handler = panic::take_handler();
panic::set_handler(|_| ());
let _res = recover(|| {
panic!("fail");
});
panic::set_handler(move |info| { (*orig_handler)(info) });
}));
}
let results: Vec<_> = build.into_iter().map(|jh| jh.join()).collect();
let all_passed = results.into_iter().all(|r| r.is_ok());
println!("{}", all_passed);
}When I run this, I get the following output:
(other runs produce more output, or garbled output, but always fewer than 10 total lines) Given that I'm running 10 threads, I'd expect to either 10 lines of output, or 0, but not two. Is this an incorrect usage of the API, or is this actually a bug I'm seeing? edit: versions
|
This comment has been minimized.
This comment has been minimized.
|
Having |
This comment has been minimized.
This comment has been minimized.
|
@samphippen The panic handler is a global resource. If multiple threads are messing with it at once without synchronization, you'll see that kind of behavior. You will want to take a different approach, possibly involving a handler that looks at a thread local which indicates if the panic should be suppressed. |
This comment has been minimized.
This comment has been minimized.
yberreby
commented
Jan 17, 2016
|
Now that |
This comment has been minimized.
This comment has been minimized.
|
@SimonSapin Those kinds of race conditions still exist if setting and taking were not separate actions. If multiple threads are adjusting a global resource at once without any synchronization at once, you'll see inconsistent behavior. For example, if the panic handlers were a stack, and each handler could choose whether or not to let the next handler run, the ordering of that stack would be inconsistent and so if any handler doesn't unconditionally allow the next handler to run, you'll see inconsistent behavior. @yberreby |
This comment has been minimized.
This comment has been minimized.
|
The issue with resetting an old handler is a problem - it's not great to force it to get wrapped in another closure every time. It seems like adjusting EDIT: Ah, it's actually a bit weirder since it's more like |
This comment has been minimized.
This comment has been minimized.
yberreby
commented
Jan 17, 2016
|
@sfackler Oh? I thought it had been. My mistake.
Couldn't all of this be solved rather easily, especially since
You usually don't want to have dependencies mess with a global resource, but the current approach encourages this behavior. If you don't want caught panics to be silent, you can always re-throw them and have them bubble up to the main thread, can't you? |
This comment has been minimized.
This comment has been minimized.
|
"Just one panic handler" sounds like a global resource to me. How would you configure it? It's absolutely true you don't want dependencies messing with the global handler whenever they want, but you absolutely do want it to be possible for dependencies to adjust the panic handler when asked. For example, you may want your EDIT: Ah, and as I mentioned in a previous comment, it is crucial that you be able to capture backtraces at the point that you "deal" with a panic, whether that be in a panic handler or elsewhere. |
This comment has been minimized.
This comment has been minimized.
yberreby
commented
Jan 17, 2016
|
@sfackler You're right, it should be part of "Just one panic handler" is a global resource, like, say, environment variables. However, with this approach, it's less problematic because Moreover, we already had a global panic handler. There needs to be one to provide logging in case of completely unhandled panic. It's not strictly needed for it to be writable, but it's convenient for the use-case you described (panic logging), and not having to wrap main in a giant |
This comment has been minimized.
This comment has been minimized.
|
From my perspective, there are a couple things here: First, Here are a couple of examples that the panic handler API is intended to help deal with. Could you go into a bit more detail on how these kinds of things would work with your proposal? I'm not sure I totally understand it. I'm writing an e.g. webserver, and use I notice some weird panics popping up, and switch to a panic handler that also captures backtraces of the panic source. I can't track down what's going on just from a backtrace, so I switch to a panic handler that will trigger a process abort for the panic I'm debugging so I can look at the core dump in a debugger. Note that the latter two of these cases require that the code that looks at the |
This comment has been minimized.
This comment has been minimized.
|
@sfackler Yes, synchronization is needed, and I argue that libstd should provide that synchronization by default. The API could take a closure that is given a the previous handler and return the new one, and is called with a lock held internally. Something like:
Looking at the source now, access to the private Note that the first thing |
This comment has been minimized.
This comment has been minimized.
|
@SimonSapin the issue is that that kind of API doesn't provide the proper sort of synchronization. For example, the race that @samphippen ran into is still present. Fundamentally, if you have multiple threads adjusting the panic handler concurrently, it seems to me like you need an external, implementation specific ordering to be imposed for the resulting handler to be consistent. What use cases were you imagining for which an atomic |
This comment has been minimized.
This comment has been minimized.
Nope, should be fine. I don't remember any deliberate decision not to include it. |
SimonSapin
referenced this issue
Feb 1, 2016
Open
Tracking issue for std::io::set_panic and set_print #31343
alexcrichton
added
final-comment-period
and removed
I-nominated
labels
Apr 29, 2016
This comment has been minimized.
This comment has been minimized.
|
The libs team discussed this issue during triage yesterday and the decision was to stabilize |
alexcrichton
added a commit
to alexcrichton/rust
that referenced
this issue
May 17, 2016
alexcrichton
added a commit
to alexcrichton/rust
that referenced
this issue
May 17, 2016
alexcrichton
referenced this issue
May 17, 2016
Merged
std: Stabilize APIs for the 1.10 release #33699
alexcrichton
added a commit
to alexcrichton/rust
that referenced
this issue
May 18, 2016
alexcrichton
added a commit
to alexcrichton/rust
that referenced
this issue
May 18, 2016
alexcrichton
added a commit
to alexcrichton/rust
that referenced
this issue
May 18, 2016
This comment has been minimized.
This comment has been minimized.
|
When the crate is compiled with |
This comment has been minimized.
This comment has been minimized.
|
I believe it will still be called, yeah - is that right @alexcrichton? I'll update the docs. |
This comment has been minimized.
This comment has been minimized.
|
Yes hooks are called regardless of the panic runtime |
alexcrichton commentedDec 17, 2015
Tracking issue for rust-lang/rfcs#1328