Pass command panics to the fallback error handler#24911
Conversation
Construct a static `CommandMeta` for each type so they do not take up extra space in the buffer.
SpecificProtagonist
left a comment
There was a problem hiding this comment.
Looks great; recovering as early as possible when the fallback error handler is set to swallow the panic is the right thing to do.
…field in the `CommandRunner`.
| runner.local_cursor += size_of::<C>(); | ||
| #[cfg(all(feature = "debug", feature = "std"))] | ||
| { | ||
| runner.current_command_name = core::any::type_name::<C>(); |
There was a problem hiding this comment.
I kinda wish DebugName worked like MaybeLocation where it is generic with a good default type but you could change it, like to &'static str in this case.
| queue.push(SpawnCommand); | ||
|
|
||
| let mut world = World::new(); | ||
| world.insert_resource(FallbackErrorHandler(warn)); |
There was a problem hiding this comment.
This test and the other basically only test how the program continues after the panic was kind of ignored. Since this PR is about passing the error to the handler, I would like a more explicit test where you add a custom handler that asserts that it actually received the panic with the specific message. Such a test would also need to assert the handler was used, maybe by flipping a static AtomicBool.
There was a problem hiding this comment.
This test and the other basically only test how the program continues after the panic was kind of ignored. Since this PR is about passing the error to the handler, I would like a more explicit test where you add a custom handler that asserts that it actually received the panic with the specific message. Such a test would also need to assert the handler was used, maybe by flipping a static
AtomicBool.
Oh, that's a very good idea!
... And having started writing a test there, I now discover that the context wasn't actually getting passed because the debug feature is on bevy_utils and not bevy_ecs. Which ties into your other comment :).
Let me try one more rewrite on the context passing. I didn't want to put the unwind handling in the generic code because it would get monomorphized a lot, but I think if I just call catch_unwind and DebugName::type_name there and then extract everything else to a #[cold] non-generic function that it shouldn't cause very much codegen.
…ble, but move all the handling code into a `#[cold]` function so that it is not monomorphized.
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 #24240. This means if the error handler is set to something non-panicking like
warn, thenflush_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_sizebefore applying the command.