Pass panics to the fallback error handler#24240
Conversation
| Err(payload) if PANIC_ORIGINATES_FROM_ERROR_HANDLER.replace(false) => { | ||
| (None, Err(payload)) | ||
| } | ||
| // We can ignore the panic payload here, as it will have already been printed. |
There was a problem hiding this comment.
By default, the panic handler will have already printed the panic payload, but we could add it to the error message given to the error handler too.
| Err(_) => { | ||
| let err = BevyError::new_with_backtrace( | ||
| Severity::Panic, | ||
| "System panicked", |
There was a problem hiding this comment.
I'm using only an error message here, but if desired I could create a structured error that can be BevyError::downcast_refed.
Edit: The panic payload isn't Sync, so it can't be included in the error type, so this wouldn't be particularly useful
chescock
left a comment
There was a problem hiding this comment.
Yeah, being able to recover from panics seems like a really important part of making Bevy robust!
|
It looks like your PR has been selected for a highlight in the next release blog post, but you didn't provide a release note. Please review the instructions for writing release notes, then expand or revise the content in the release notes directory to showcase your changes. |
chescock
left a comment
There was a problem hiding this comment.
Thanks, this version was much easier for me to follow!
|
Oh, I just remembered run conditions! Do we need to do something similar for them? I don't think we even |
Co-authored-by: Gonçalo Rica Pais da Silva <bluefinger@gmail.com>
Co-authored-by: Gonçalo Rica Pais da Silva <bluefinger@gmail.com>
# Objective Improve the handling of commands that panic. Currently, if a command panics, any unapplied commands are left in the command queue. This means they will get run, but at an unpredictable time in the future. And when running in `no_std`, cleanup is not run at all and the commands simply leak. ## Solution Catch panics from commands and pass them to the fallback error handler, just as we now do for systems from bevyengine#24240. This means if the error handler is set to something non-panicking like `warn`, then `flush_commands()` will never panic and all commands in the queue will be applied. Introduce a RAII guard during command application that will drop any commands still in the queue and perform cleanup during unwinding, even on `no_std`. To make the command's type name available in the error handler, add a field to the RAII guard that gets written by `consume_command_and_get_size` before applying the command.
Objective
Currently, a panic (whether from engine or user code, as there is little distinction) takes down the entire app with it. Instead the user should be able to decide how the error is handled. This is currently not possible except by writing your own executor and setting it for all relevant schedules.
See for comparison Godot's policy on exceptions:
Unity will also log an error and then continue if user code throws an exception. I believe Unreal does too for exceptions coming from Blueprints. Similarly, many web servers will respond with an error to a request that threw an exception, but will not crash the server itself.
This PR does not enable this behavior by default, but makes it user-configurable.
Also fixes #19109
Also (I think) fixes #7434
Solution
Instead of rethrowing panics, hand them to the
FallbackErrorHandler.If the panic was thrown by an error handler in the first place, we don't need to pass it back to a handler again. I've added a way for the error handler to signal that it's the source of the panic.
The constructed error is created without a backtrace, as the default panic handler already prints it when instructed to via
RUST_LIB_BACKTRACE/RUST_BACKTRACE.Panics will not be turned into errors on
no_stdprojects.Potential work for a future PR:
queue_handled, use this error handler instead of the fallback error handlerTesting
See added
panic_to_errortest