-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add thread-local custom panic handlers to customize the behavior of thread panics #1100
Conversation
Related: #1089
It should be noted, that state of (silent) panic is not entirely undetectable: join on thread returns Err side of Result and main thread exits the process with a certain error code. |
panic!()
silent when called without argumentspanic!()
output nothing when called without arguments
This sounds great to me. |
This doesn't need to be implemented as another rule to In the |
Why change the behavior of plain Panics can carry arbitrary values so in libstd style I guess we could have a I'm not sure how feasible this is to implement, but as you know the panic value is wrapped in Edit: You can totally add the check for a panic options here in the code and silence the panic message. |
FWIW, I don't think making this change should be considered a violation of stability. It wouldn't affect the API, so there's no backwards-compatibility issues as far as compilation goes, and I'm finding it hard to imagine how turning off a message printed to stderr on task panic would break anybody. That said, I'd still prefer to have to opt-in to silent panics. The idea of passing a panic configuration value to |
@kballard It breaks the invariant that |
@cybergeek94 Printing a message to stderr is not part of the documentation for |
So having |
One more thing: even though we consciously and infamously support "catching" unwinding, that doesn't mean we should be encouraging that use as a normal error-handling solution. A panic at runtime indicates a problem, and if you're really panicking so often that you're clogging up your logfiles then I need to ask what your architecture is like that you feel the need to resort to that approach. |
@kballard Panicking with a known value for panic_silent! is of course a reasonable idea. I wanted to point out that panicking with values is already exposed, you can pass an arbitrary value to |
Instead of having panic_silent!(), might it make sense to have panic!(panic::silent) or something? Basically add a value that isn't currently accepted to panic!(). |
Could changing the output of plain FWIW, I think This way we don't lose debugging information on panics, and we can have tight code in release builds. |
I think there is a related problem which makes panic not very useful for messaging failure and that there is nothing it can carry. It's very hard for a panic handler to figure out why exactly something panicked. You can't even set an integer to signal something upwards. I think there is more that needs doing than just removing an error message. |
@bluss is right, the logging lies in From what has been brought up, I think the best solution is as follow:
Here's how it would be used: use std::thread;
fn main() {
thread::spawn(|| {
// use the default panic handler, `debug`, the one we currently have
some_optional.unwrap();
}).join();
thread::spawn(|| {
// Always use the `silent` handler
thread::on_panic(thread::handlers::silent);
// This will never display a message
// Use cases:
// - abort thread silently on errors unrecoverable at its level,
// but recoverable at the process level, and spawn a new one
a_third_party_function_that_could_fail();
}).join();
thread::spawn(|| {
// Always use the `basic` handler
thread::on_panic(thread::handlers::basic);
// This will simply display whatever is passed to `panic!` on panic,
// much like println!()
// Use cases:
// - abort thread on invalid input with a user-friendly message
if !is_valid(input) {
panic!("Invalid input - please try again");
} else {
// ...
}
}).join();
} This would undoubtedly offer the most flexibility, without breaking backward compatibility. Additionally, To have debug messages on debug builds and bare-bones messages on release builds, users could simply do this: thread::on_panic(
if cfg!(debug) {
thread::handlers::debug
} else {
thread::handlers::basic
}
); Or this could be the default, but I'm not sure rustc supports debug flags, or whether this would be desirable. What do you think? PS: I'm not familiar with the RFC process - should I update the RFC to integrate these changes, even if it implies changing the feature name from |
Or carry none.
Changing that to
You already can replace panic handler to your own by not using libstd and providing your own
Its your RFC, you may change it anytime until it is accepted. Just note what changes you made and keep commit history. From both implementation complexity and API standpoint I, personally, prefer original suggestion. |
This is arguably not handy for something as simple as changing what is output to
Glad to know that! I'm working on the second iteration.
It surely is simpler, but it's less customizable and flexible. I believe |
panic!()
output nothing when called without argumentspanic!()
to stderr with 'panic handlers'
I've heavily modified the RFC in the last commit. |
1 |
1, although there are a few things that need clarifying:
|
-1. What bstrie said on encouraging use of panic. Catching panics should only be done for esoteric FFI reasons. |
@Ericson2314 And yet we are currently allowing panic catching. The standard library accounts for panics (mutex poisoning comes to my mind). A lot of library functions can and do panic. If we are to drop support for clean panicking, we should drop support for We need to be consistent. Either we support panics, or we don't. If we do, we need to do it right. Unwinding a thread's stack and running destructors has uses, even though I would often prefer a @Diggsey I doubt I'm qualified enough, but your questions alone shed light on potential problems, so it would probably better to leave refactoring of the handler mechanism (which already exists, although it's unstable) for a later RFC, and change this one to a simple logging mode selection, which changes the behavior of the I'll update the RFC, thanks for your comments. |
An alternative to a |
@sfackler yeah as @Diggsey mentioned we run these handlers currently before unwinding, so the double-panic part may come into play. I'm somewhat leaning more towards only having a global handler as it allows building thread-local handlers on top, so in that sense it seems more flexible. Along those lies I'd want the handler to be |
1 for this. Writing an ugly message to stderr isn't the best thing to do when you have a bug. |
1 My current problem is that I am working on a library using coroutines, that is supposed to handle panics of coroutines it manages. It also implements tests that exercises panic conditions in multithreading cases (so the terminal gets severely trashed). |
@dpc Of course as we know from certain logging frameworks the right thing to do in such cases is to use an MPSC queue thread to write the messages. |
@llogiq : I don't understand. |
Your problem is that multiple threads may write to the console. To avoid trashing the terminal with concurrent writes, you need to synchronize the output. Now there are two ways to do that: Either have the panic output lock the console (which has its own set of problems), or send the stack info to a helper thread that your library spins up to output the stack traces. The panic helper thread then becomes the synchronization point. Of course to send the stack infos, one needs some kind of MPSC queue (e.g. a channel). |
I don't want to synchronize anything. I'm not printing anything. It's just |
So you want to silence |
Yes. I just wanted to state my usecase, and why I 1 this RFC. |
👍 Cool stuff. |
When logging to stderr you can rely on |
What's the status of this? |
@arielb1 As per #1100 (comment), there are some problems with my last proposal. The idea itself - custom panic handler - seems to have been well received, but the proposed implementation is far from perfect. Unfortunately, my studies take up a lot of time so I probably won't be able to come up with a new, reasonably good implementation spec in the following months. So unless somebody else does, well, it may take a long time for this to be accepted and implemented. |
cc @ranma42 (I started doing some work on TLS and panicking, I might be able to contribute if I get some spare time) |
I'm all for this PR. I help develop a terminal graphics library and stderr is not a valid stream we want to be printing exceptions to. We currently have no tools to copy / redirect stderr in rust, this would allow us to properly do so |
This RFC still wouldn’t allow copying or redirecting stderr per se. It is, however, already possible on at least POSIX systems by using use std::os::raw::c_int;
extern { fn dup2(fd1: c_int, fd2: c_int) };
fn main(){
// create a fd
unsafe { assert!(dup2(your_fd, 2) != -1); }
} |
For those interested, @sfackler has posted an alternate RFC to this one which is more centered around global handlers. |
🔔 This RFC is now entering its week-long final comment period 🔔 Note that this entering FCP at the same time as #1328 and the two will likely be decided upon as a unit. |
@alexcrichton I'm pretty sure this RFC can't be merged as-is, and I wouldn't want it to, since the issues @sfackler's pointed out haven't been addressed. Should I close it now? |
It's true that the libs team would be unlikely to merge this as-is, but it represents an alternative strategy from #1328 that we're willing to consider. Along those lines we're fine leaving this open as a proxy for the "thread local strategy" vs the "global handler strategy". |
The libs team discussed this RFC during triage yesterday and the conclusion was to merge #1328 and close this one. Thanks again for the RFC though @yberreby! |
Rendered
EDIT: heavily modified since the first commits!
EDIT 2 (3rd of July): provided implementation details and PoC (see #1100 (comment))