Skip to content
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

Cleaned up panic messages #8219

Merged
merged 15 commits into from
Apr 12, 2023
Merged

Conversation

JonahPlusPlus
Copy link
Contributor

@JonahPlusPlus JonahPlusPlus commented Mar 26, 2023

Objective

Fixes #8215 and #8152. When systems panic, it causes the main thread to panic as well, which clutters the output.

Solution

Resolves the panic in the multi-threaded scheduler. Also adds an extra message that tells the user the system that panicked.

Using the example from the issue, here is what the messages now look like:

use bevy::prelude::*;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_systems(Update, panicking_system)
        .run();
}

fn panicking_system() {
    panic!("oooh scary");
}

Before

   Compiling bevy_test v0.1.0 (E:\Projects\Rust\bevy_test)
    Finished dev [unoptimized + debuginfo] target(s) in 2m 58s
     Running `target\debug\bevy_test.exe`
2023-03-30T22:19:09.234932Z  INFO bevy_diagnostic::system_information_diagnostics_plugin::internal: SystemInfo { os: "Windows 10 Pro", kernel: "19044", cpu: "AMD Ryzen 5 2600 Six-Core Processor", core_count: "6", memory: "15.9 GiB" }
thread 'Compute Task Pool (5)' panicked at 'oooh scary', src\main.rs:11:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread 'Compute Task Pool (5)' panicked at 'A system has panicked so the executor cannot continue.: RecvError', E:\Projects\Rust\bevy\crates\bevy_ecs\src\schedule\executor\multi_threaded.rs:194:60
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', E:\Projects\Rust\bevy\crates\bevy_tasks\src\task_pool.rs:376:49
error: process didn't exit successfully: `target\debug\bevy_test.exe` (exit code: 101)

After

   Compiling bevy_test v0.1.0 (E:\Projects\Rust\bevy_test)
    Finished dev [unoptimized + debuginfo] target(s) in 2.39s
     Running `target\debug\bevy_test.exe`
2023-03-30T22:11:24.748513Z  INFO bevy_diagnostic::system_information_diagnostics_plugin::internal: SystemInfo { os: "Windows 10 Pro", kernel: "19044", cpu: "AMD Ryzen 5 2600 Six-Core Processor", core_count: "6", memory: "15.9 GiB" }
thread 'Compute Task Pool (5)' panicked at 'oooh scary', src\main.rs:11:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Encountered a panic in system `bevy_test::panicking_system`!
Encountered a panic in system `bevy_app::main_schedule::Main::run_main`!
error: process didn't exit successfully: `target\debug\bevy_test.exe` (exit code: 101)

@alice-i-cecile alice-i-cecile added this to the 0.11 milestone Mar 26, 2023
@alice-i-cecile alice-i-cecile added A-ECS Entities, components, systems, and events C-Usability A targeted quality-of-life change that makes Bevy easier to use labels Mar 26, 2023
JonahPlusPlus and others added 2 commits March 27, 2023 01:33
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
@JonahPlusPlus
Copy link
Contributor Author

JonahPlusPlus commented Mar 27, 2023

I'm going to try to fix #8152 at the same time. Many of the panics that cause noise are necessary for making sure the application actually panics, and not merely quits (exit code 0 vs 101). For example, the panic I'm having difficulty with right now is one in bevy_tasks. I would like to make it so that whatever panic was captured is propagated from here, but that means finding some way of storing/retrieving the payload.

I was able to fix a similar issue in bevy_ecs by using a Arc<Mutex<Option<Box<(dyn Any + Send)>>>> (just rolls off the tongue), but this isn't very idiomatic and it would be better to replace Receiver with something that returns Box<(dyn Any + Send)> (likely wrapped in something that is Sync by guaranteeing it's a &'static str or String) instead of RecvError.

In the case of the bevy_tasks dilemma, FallibleTask probably needs to be replaced with some sort of task that returns Result<T, Box<(dyn Any + Send)>>.

These issues may also be fixed by refactoring the design. In the meantime, I'm going to try creating these alternative types.

@JonahPlusPlus
Copy link
Contributor Author

Working on a PanickyTask type and noticed the latest version of async-tasks has a builder type that has an option for propagating panics. Unfortunately, async-executor doesn't expose this API, making it unusable. I'm still going to work on PanickyTask, but it might be worth making a PR upstream.

@hymm
Copy link
Contributor

hymm commented Mar 29, 2023

I think instead of closing the channel to propagate the error, we should be trying a more graceful shutdown. Specifically we should be waiting for any currently running systems to finish before stopping the multithreaded executor. This could be done by changing the finish channel to use a result instead of just sending the index and then handling the error in the executor.

@JonahPlusPlus
Copy link
Contributor Author

@hymm Yeah, we can do that. I suppose the benefit would be so IO tasks don't corrupt files/send corrupted messages? Are there any other benefits to be aware of? There will be a cost of channel messages doubling in size (right now, the added cost is checking a mutex which is guaranteed to not be locked until a panic), so I would like to get an overview of the pros and cons of such a design.

@JonahPlusPlus
Copy link
Contributor Author

JonahPlusPlus commented Mar 30, 2023

Never mind, I was able to avoid the cost in the channels by keeping it the same. The only difference now is that systems always signal that they are complete, regardless of whether they were successful or not. Instead of closing the channel, they just set the panic payload.

Edit: However, I'm not sure how this change will affect dependent systems. Essentially, the change I made makes it so systems signal that they have finished regardless of whether or not they are successful. This would mean dependent systems are signaled that they can start, which might cause further panics.

Edit 2: Yeah, tested this with a simple app where there are two systems, where one depends on a resource inserted by the first system. If that first system panics, the second one panics as well. It seems that cost in channels is unavoidable, since there needs to be some way of signaling to the executor that the system finished/failed without signaling to dependent systems to start.

@JonahPlusPlus
Copy link
Contributor Author

JonahPlusPlus commented Mar 30, 2023

Okay, it now allows unfinished systems to complete. That includes systems not even started yet (in the running schedules), as long as they don't depend on the panicked system. The reason for this is that advanced behavior can be split into multiple systems. If we were to panic immediately or only allow systems that were started to finish, systems that may be responsible for cleaning up those systems would never run, which could lead to issues in IO, like file corruption. There may be other reasons for doing this that I'm not aware about.

I still have to fix a few other bugs + add some optimizations.

@JonahPlusPlus JonahPlusPlus marked this pull request as ready for review March 30, 2023 19:33
@JonahPlusPlus
Copy link
Contributor Author

JonahPlusPlus commented Mar 30, 2023

Okay, I think this is ready. Errors now properly propagate up (though it looks like some of the backtrace is still missing, but I think that's intended due to how bevy_tasks restarts tasks that panic; this is my first look at the internals of bevy_tasks, so I would need someone to confirm this) and all systems (in the schedule) run before exiting (if possible).

@JonahPlusPlus
Copy link
Contributor Author

JonahPlusPlus commented Mar 30, 2023

Oops, found a bug: I merely copied the code from skipping systems for skipping dependents, but I should have ensured dependents of dependents are skipped as well.

Edit: I'll add it as a test for future reference.

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
@mockersf
Copy link
Member

mockersf commented Mar 30, 2023

Could you update the description with how a panic would look like?

What happens if more than one system panics at the same time in different threads?

@JonahPlusPlus
Copy link
Contributor Author

JonahPlusPlus commented Mar 30, 2023

@mockersf

Could you update the description with how a panic would look like?

Will do.

What happens if more than one system panics at the same time in different threads?

Here is a small program:

use bevy::prelude::*;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_systems(Update, (panicking_system, panicking_system_2))
        .run();
}

fn panicking_system() {
    panic!("oooh scary");
}

fn panicking_system_2() {
    panic!("oooh scary 2!");
}

And the output:

   Compiling bevy_test v0.1.0 (E:\Projects\Rust\bevy_test)
    Finished dev [unoptimized + debuginfo] target(s) in 2.71s
     Running `target\debug\bevy_test.exe`
2023-03-30T21:40:14.414278Z  INFO bevy_diagnostic::system_information_diagnostics_plugin::internal: SystemInfo { os: "Windows 10 Pro", kernel: "19044", cpu: "AMD Ryzen 5 2600 Six-Core Processor", core_count: "6", memory: "15.9 GiB" }
thread 'thread 'Compute Task Pool (5)Compute Task Pool (2)' panicked at '' panicked at 'oooh scaryoooh scary 2!', ', src\main.rssrc\main.rs::1115::55

note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Encountered a panic in system `bevy_test::panicking_system_2`!
Encountered a panic in system `bevy_test::panicking_system`!
Encountered a panic in system `bevy_app::main_schedule::Main::run_main`!
error: process didn't exit successfully: `target\debug\bevy_test.exe` (exit code: 101)

That last message is due to the fact that there are two schedules running. The main, single-threaded schedule and the the multi-threaded on top of it. For that reason, the error is caught twice and prints two messages. I rephrased the error messages so it doesn't sound like the main thread panicked when it was a task somewhere in the main thread that panicked. This wasn't really intended, but is more of a happy little accident, since it gives a mini backtrace of sorts.

@mockersf
Copy link
Member

thread 'thread 'Compute Task Pool (5)Compute Task Pool (2)' panicked at '' panicked at 'oooh scaryoooh scary 2!', ', src\main.rssrc\main.rs::1115::55

That doesn't look good 😄

What's the stack trace displayed with RUST_BACKTRACE?

@JonahPlusPlus
Copy link
Contributor Author

JonahPlusPlus commented Mar 30, 2023

@mockersf

That doesn't look good 😄

Yeah, but IDK if there is any thing we can do there. I think it's more a problem with panic! printing both at once. Edit: it should be a rare occurence that two threads panic at the exact same time.

    Finished dev [unoptimized + debuginfo] target(s) in 0.49s
     Running `target\debug\bevy_test.exe`
2023-03-30T21:46:29.398210Z  INFO bevy_diagnostic::system_information_diagnostics_plugin::internal: SystemInfo { os: "Windows 10 Pro", kernel: "19044", cpu: "AMD Ryzen 5 2600 Six-Core Processor", core_count: "6", memory: "15.9 GiB" }
thread 'thread 'Compute Task Pool (4)Compute Task Pool (0)' panicked at '' panicked at 'oooh scaryoooh scary 2!', ', src\main.rssrc\main.rs::1115::55

stack backtrace:
   0: std::panicking::begin_panic_handler
             at /rustc/8460ca823e8367a30dda430efda790588b8c84d3/library\std\src\panicking.rs:575
   1: core::panicking::panic_fmt
             at /rustc/8460ca823e8367a30dda430efda790588b8c84d3/library\core\src\panicking.rs:64
   2: bevy_test::panicking_system
             at .\src\main.rs:11
   3: core::ops::function::FnMut::call_mut<void (*)(),tuple$<> >
             at /rustc/8460ca823e8367a30dda430efda790588b8c84d3\library\core\src\ops\function.rs:166
   4: core::ops::function::impls::impl$3::call_mut<tuple$<>,void (*)()>
             at /rustc/8460ca823e8367a30dda430efda790588b8c84d3\library\core\src\ops\function.rs:298
   5: bevy_ecs::system::function_system::impl$10::run::call_inner<tuple$<>,ref_mut$<void (*)()> >
             at E:\Projects\Rust\bevy\crates\bevy_ecs\src\system\function_system.rs:648
   6: bevy_ecs::system::function_system::impl$10::run<tuple$<>,void (*)()>
             at E:\Projects\Rust\bevy\crates\bevy_ecs\src\system\function_system.rs:651
   7: bevy_ecs::system::function_system::impl$7::run_unsafe<void (*)(),void (*)()>
             at E:\Projects\Rust\bevy\crates\bevy_ecs\src\system\function_system.rs:485
   8: bevy_ecs::schedule::executor::multi_threaded::impl$3::spawn_system_task::async_block$0::closure$0
             at E:\Projects\Rust\bevy\crates\bevy_ecs\src\schedule\executor\multi_threaded.rs:470
   9: core::ops::function::FnOnce::call_once<bevy_ecs::schedule::executor::multi_threaded::impl$3::spawn_system_task::async_block$0::closure_env$0,tuple$<> >
             at /rustc/8460ca823e8367a30dda430efda790588b8c84d3\library\core\src\ops\function.rs:250
  10: core::panic::unwind_safe::impl$23::call_once<tuple$<>,bevy_ecs::schedule::executor::multi_threaded::impl$3::spawn_system_task::async_block$0::closure_env$0>
             at /rustc/8460ca823e8367a30dda430efda790588b8c84d3\library\core\src\panic\unwind_safe.rs:271
  11: std::panicking::try::do_call<core::panic::unwind_safe::AssertUnwindSafe<bevy_ecs::schedule::executor::multi_threaded::impl$3::spawn_system_task::async_block$0::closure_env$0>,tuple$<> >
             at /rustc/8460ca823e8367a30dda430efda790588b8c84d3\library\std\src\panicking.rs:483
  12: std::panicking::try::do_catch<core::panic::unwind_safe::AssertUnwindSafe<bevy_ecs::schedule::executor::multi_threaded::impl$3::spawn_system_task::async_block$0::closure_env$0>,tuple$<> >
  13: std::panicking::try<tuple$<>,core::panic::unwind_safe::AssertUnwindSafe<bevy_ecs::schedule::executor::multi_threaded::impl$3::spawn_system_task::async_block$0::closure_env$0> >
             at /rustc/8460ca823e8367a30dda430efda790588b8c84d3\library\std\src\panicking.rs:447
  14: std::panic::catch_unwind<core::panic::unwind_safe::AssertUnwindSafe<bevy_ecs::schedule::executor::multi_threaded::impl$3::spawn_system_task::async_block$0::closure_env$0>,tuple$<> >
             at /rustc/8460ca823e8367a30dda430efda790588b8c84d3\library\std\src\panic.rs:140
  15: bevy_ecs::schedule::executor::multi_threaded::impl$3::spawn_system_task::async_block$0
             at E:\Projects\Rust\bevy\crates\bevy_ecs\src\schedule\executor\multi_threaded.rs:468
  16: core::panic::unwind_safe::impl$26::poll<enum2$<bevy_ecs::schedule::executor::multi_threaded::impl$3::spawn_system_task::async_block_env$0> >
             at /rustc/8460ca823e8367a30dda430efda790588b8c84d3\library\core\src\panic\unwind_safe.rs:296
  17: futures_lite::future::impl$14::poll::closure$0<core::panic::unwind_safe::AssertUnwindSafe<enum2$<bevy_ecs::schedule::executor::multi_threaded::impl$3::spawn_system_task::async_block_env$0> > >
             at C:\Users\jonah\.cargo\registry\src\github.com-1ecc6299db9ec823\futures-lite-1.12.0\src\future.rs:626
  18: core::panic::unwind_safe::impl$23::call_once<enum2$<core::task::poll::Poll<tuple$<> > >,futures_lite::future::impl$14::poll::closure_env$0<core::panic::unwind_safe::AssertUnwindSafe<enum2$<bevy_ecs::schedule::executor::multi_threaded::impl$3::spawn_system
             at /rustc/8460ca823e8367a30dda430efda790588b8c84d3\library\core\src\panic\unwind_safe.rs:271
  19: std::panicking::try::do_call<core::panic::unwind_safe::AssertUnwindSafe<futures_lite::future::impl$14::poll::closure_env$0<core::panic::unwind_safe::AssertUnwindSafe<enum2$<bevy_ecs::schedule::executor::multi_threaded::impl$3::spawn_system_task::async_blo
             at /rustc/8460ca823e8367a30dda430efda790588b8c84d3\library\std\src\panicking.rs:483
  20: std::panicking::try::do_catch<core::panic::unwind_safe::AssertUnwindSafe<bevy_ecs::schedule::executor::multi_threaded::impl$3::spawn_system_task::async_block$0::closure_env$0>,tuple$<> >
  21: std::panicking::try<enum2$<core::task::poll::Poll<tuple$<> > >,core::panic::unwind_safe::AssertUnwindSafe<futures_lite::future::impl$14::poll::closure_env$0<core::panic::unwind_safe::AssertUnwindSafe<enum2$<bevy_ecs::schedule::executor::multi_threaded::im
             at /rustc/8460ca823e8367a30dda430efda790588b8c84d3\library\std\src\panicking.rs:447
  22: std::panic::catch_unwind<core::panic::unwind_safe::AssertUnwindSafe<futures_lite::future::impl$14::poll::closure_env$0<core::panic::unwind_safe::AssertUnwindSafe<enum2$<bevy_ecs::schedule::executor::multi_threaded::impl$3::spawn_system_task::async_block_e
             at /rustc/8460ca823e8367a30dda430efda790588b8c84d3\library\std\src\panic.rs:140
  23: futures_lite::future::impl$14::poll<core::panic::unwind_safe::AssertUnwindSafe<enum2$<bevy_ecs::schedule::executor::multi_threaded::impl$3::spawn_system_task::async_block_env$0> > >
             at C:\Users\jonah\.cargo\registry\src\github.com-1ecc6299db9ec823\futures-lite-1.12.0\src\future.rs:626
  24: async_executor::impl$4::spawn::async_block$0<enum2$<core::result::Result<tuple$<>,alloc::boxed::Box<dyn$<core::any::Any,core::marker::Send>,alloc::alloc::Global> > >,futures_lite::future::CatchUnwind<core::panic::unwind_safe::AssertUnwindSafe<enum2$<bevy_
             at C:\Users\jonah\.cargo\registry\src\github.com-1ecc6299db9ec823\async-executor-1.5.0\src\lib.rs:139
  25: async_task::raw::RawTask<enum2$<async_executor::impl$4::spawn::async_block_env$0<enum2$<core::result::Result<tuple$<>,alloc::boxed::Box<dyn$<core::any::Any,core::marker::Send>,alloc::alloc::Global> > >,futures_lite::future::CatchUnwind<core::panic::unwind
             at C:\Users\jonah\.cargo\registry\src\github.com-1ecc6299db9ec823\async-task-4.3.0\src\raw.rs:511
  26: async_task::runnable::Runnable::run
             at C:\Users\jonah\.cargo\registry\src\github.com-1ecc6299db9ec823\async-task-4.3.0\src\runnable.rs:309
  27: async_executor::impl$4::run::async_fn$0::async_block$0<enum2$<core::result::Result<tuple$<>,async_channel::RecvError> >,futures_lite::future::Or<enum2$<bevy_tasks::task_pool::impl$2::new_internal::closure$0::closure$0::closure$0::closure$0::async_block_en
             at C:\Users\jonah\.cargo\registry\src\github.com-1ecc6299db9ec823\async-executor-1.5.0\src\lib.rs:230
  28: futures_lite::future::impl$12::poll<enum2$<core::result::Result<tuple$<>,async_channel::RecvError> >,futures_lite::future::Or<enum2$<bevy_tasks::task_pool::impl$2::new_internal::closure$0::closure$0::closure$0::closure$0::async_block_env$0>,async_channel:
             at C:\Users\jonah\.cargo\registry\src\github.com-1ecc6299db9ec823\futures-lite-1.12.0\src\future.rs:529
  29: async_executor::impl$4::run::async_fn$0<enum2$<core::result::Result<tuple$<>,async_channel::RecvError> >,futures_lite::future::Or<enum2$<bevy_tasks::task_pool::impl$2::new_internal::closure$0::closure$0::closure$0::closure$0::async_block_env$0>,async_chan
             at C:\Users\jonah\.cargo\registry\src\github.com-1ecc6299db9ec823\async-executor-1.5.0\src\lib.rs:237
  30: futures_lite::future::block_on::closure$0<enum2$<core::result::Result<tuple$<>,async_channel::RecvError> >,enum2$<async_executor::impl$4::run::async_fn_env$0<enum2$<core::result::Result<tuple$<>,async_channel::RecvError> >,futures_lite::future::Or<enum2$<
             at C:\Users\jonah\.cargo\registry\src\github.com-1ecc6299db9ec823\futures-lite-1.12.0\src\future.rs:89
  31: std::thread::local::LocalKey<core::cell::RefCell<tuple$<parking::Parker,core::task::wake::Waker> > >::try_with<core::cell::RefCell<tuple$<parking::Parker,core::task::wake::Waker> >,futures_lite::future::block_on::closure_env$0<enum2$<core::result::Result<
             at /rustc/8460ca823e8367a30dda430efda790588b8c84d3\library\std\src\thread\local.rs:446
  32: std::thread::local::LocalKey<core::cell::RefCell<tuple$<parking::Parker,core::task::wake::Waker> > >::with<core::cell::RefCell<tuple$<parking::Parker,core::task::wake::Waker> >,futures_lite::future::block_on::closure_env$0<enum2$<core::result::Result<tupl
             at /rustc/8460ca823e8367a30dda430efda790588b8c84d3\library\std\src\thread\local.rs:422
  33: futures_lite::future::block_on<enum2$<core::result::Result<tuple$<>,async_channel::RecvError> >,enum2$<async_executor::impl$4::run::async_fn_env$0<enum2$<core::result::Result<tuple$<>,async_channel::RecvError> >,futures_lite::future::Or<enum2$<bevy_tasks:
             at C:\Users\jonah\.cargo\registry\src\github.com-1ecc6299db9ec823\futures-lite-1.12.0\src\future.rs:79
  34: bevy_tasks::task_pool::impl$2::new_internal::closure$0::closure$0::closure$0::closure$0
             at E:\Projects\Rust\bevy\crates\bevy_tasks\src\task_pool.rs:171
  35: std::panicking::try::do_call<bevy_tasks::task_pool::impl$2::new_internal::closure$0::closure$0::closure$0::closure_env$0,enum2$<core::result::Result<tuple$<>,async_channel::RecvError> > >
             at /rustc/8460ca823e8367a30dda430efda790588b8c84d3\library\std\src\panicking.rs:483
  36: std::panicking::try::do_catch<core::panic::unwind_safe::AssertUnwindSafe<std::thread::impl$0::spawn_unchecked_::closure$1::closure_env$0<bevy_tasks::task_pool::impl$2::new_internal::closure$0::closure_env$0,tuple$<> > >,tuple$<> >
  37: std::panicking::try<enum2$<core::result::Result<tuple$<>,async_channel::RecvError> >,bevy_tasks::task_pool::impl$2::new_internal::closure$0::closure$0::closure$0::closure_env$0>
             at /rustc/8460ca823e8367a30dda430efda790588b8c84d3\library\std\src\panicking.rs:447
  38: std::panic::catch_unwind<bevy_tasks::task_pool::impl$2::new_internal::closure$0::closure$0::closure$0::closure_env$0,enum2$<core::result::Result<tuple$<>,async_channel::RecvError> > >
             at /rustc/8460ca823e8367a30dda430efda790588b8c84d3\library\std\src\panic.rs:140
  39: bevy_tasks::task_pool::impl$2::new_internal::closure$0::closure$0::closure$0
             at E:\Projects\Rust\bevy\crates\bevy_tasks\src\task_pool.rs:165
  40: std::thread::local::LocalKey<async_executor::LocalExecutor>::try_with<async_executor::LocalExecutor,bevy_tasks::task_pool::impl$2::new_internal::closure$0::closure$0::closure_env$0,tuple$<> >
             at /rustc/8460ca823e8367a30dda430efda790588b8c84d3\library\std\src\thread\local.rs:446
  41: std::thread::local::LocalKey<async_executor::LocalExecutor>::with<async_executor::LocalExecutor,bevy_tasks::task_pool::impl$2::new_internal::closure$0::closure$0::closure_env$0,tuple$<> >
             at /rustc/8460ca823e8367a30dda430efda790588b8c84d3\library\std\src\thread\local.rs:422
  42: bevy_tasks::task_pool::impl$2::new_internal::closure$0::closure$0
             at E:\Projects\Rust\bevy\crates\bevy_tasks\src\task_pool.rs:158
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
stack backtrace:
Encountered a panic in system `bevy_test::panicking_system`!
   0: std::panicking::begin_panic_handler
             at /rustc/8460ca823e8367a30dda430efda790588b8c84d3/library\std\src\panicking.rs:575
   1: core::panicking::panic_fmt
             at /rustc/8460ca823e8367a30dda430efda790588b8c84d3/library\core\src\panicking.rs:64
   2: bevy_test::panicking_system_2
             at .\src\main.rs:15
   3: core::ops::function::FnMut::call_mut<void (*)(),tuple$<> >
             at /rustc/8460ca823e8367a30dda430efda790588b8c84d3\library\core\src\ops\function.rs:166
   4: core::ops::function::impls::impl$3::call_mut<tuple$<>,void (*)()>
             at /rustc/8460ca823e8367a30dda430efda790588b8c84d3\library\core\src\ops\function.rs:298
   5: bevy_ecs::system::function_system::impl$10::run::call_inner<tuple$<>,ref_mut$<void (*)()> >
             at E:\Projects\Rust\bevy\crates\bevy_ecs\src\system\function_system.rs:648
   6: bevy_ecs::system::function_system::impl$10::run<tuple$<>,void (*)()>
             at E:\Projects\Rust\bevy\crates\bevy_ecs\src\system\function_system.rs:651
   7: bevy_ecs::system::function_system::impl$7::run_unsafe<void (*)(),void (*)()>
             at E:\Projects\Rust\bevy\crates\bevy_ecs\src\system\function_system.rs:485
   8: bevy_ecs::schedule::executor::multi_threaded::impl$3::spawn_system_task::async_block$0::closure$0
             at E:\Projects\Rust\bevy\crates\bevy_ecs\src\schedule\executor\multi_threaded.rs:470
   9: core::ops::function::FnOnce::call_once<bevy_ecs::schedule::executor::multi_threaded::impl$3::spawn_system_task::async_block$0::closure_env$0,tuple$<> >
             at /rustc/8460ca823e8367a30dda430efda790588b8c84d3\library\core\src\ops\function.rs:250
  10: core::panic::unwind_safe::impl$23::call_once<tuple$<>,bevy_ecs::schedule::executor::multi_threaded::impl$3::spawn_system_task::async_block$0::closure_env$0>
             at /rustc/8460ca823e8367a30dda430efda790588b8c84d3\library\core\src\panic\unwind_safe.rs:271
  11: std::panicking::try::do_call<core::panic::unwind_safe::AssertUnwindSafe<bevy_ecs::schedule::executor::multi_threaded::impl$3::spawn_system_task::async_block$0::closure_env$0>,tuple$<> >
             at /rustc/8460ca823e8367a30dda430efda790588b8c84d3\library\std\src\panicking.rs:483
  12: std::panicking::try::do_catch<core::panic::unwind_safe::AssertUnwindSafe<bevy_ecs::schedule::executor::multi_threaded::impl$3::spawn_system_task::async_block$0::closure_env$0>,tuple$<> >
  13: std::panicking::try<tuple$<>,core::panic::unwind_safe::AssertUnwindSafe<bevy_ecs::schedule::executor::multi_threaded::impl$3::spawn_system_task::async_block$0::closure_env$0> >
             at /rustc/8460ca823e8367a30dda430efda790588b8c84d3\library\std\src\panicking.rs:447
  14: std::panic::catch_unwind<core::panic::unwind_safe::AssertUnwindSafe<bevy_ecs::schedule::executor::multi_threaded::impl$3::spawn_system_task::async_block$0::closure_env$0>,tuple$<> >
             at /rustc/8460ca823e8367a30dda430efda790588b8c84d3\library\std\src\panic.rs:140
  15: bevy_ecs::schedule::executor::multi_threaded::impl$3::spawn_system_task::async_block$0
             at E:\Projects\Rust\bevy\crates\bevy_ecs\src\schedule\executor\multi_threaded.rs:468
  16: core::panic::unwind_safe::impl$26::poll<enum2$<bevy_ecs::schedule::executor::multi_threaded::impl$3::spawn_system_task::async_block_env$0> >
             at /rustc/8460ca823e8367a30dda430efda790588b8c84d3\library\core\src\panic\unwind_safe.rs:296
  17: futures_lite::future::impl$14::poll::closure$0<core::panic::unwind_safe::AssertUnwindSafe<enum2$<bevy_ecs::schedule::executor::multi_threaded::impl$3::spawn_system_task::async_block_env$0> > >
             at C:\Users\jonah\.cargo\registry\src\github.com-1ecc6299db9ec823\futures-lite-1.12.0\src\future.rs:626
  18: core::panic::unwind_safe::impl$23::call_once<enum2$<core::task::poll::Poll<tuple$<> > >,futures_lite::future::impl$14::poll::closure_env$0<core::panic::unwind_safe::AssertUnwindSafe<enum2$<bevy_ecs::schedule::executor::multi_threaded::impl$3::spawn_system
             at /rustc/8460ca823e8367a30dda430efda790588b8c84d3\library\core\src\panic\unwind_safe.rs:271
  19: std::panicking::try::do_call<core::panic::unwind_safe::AssertUnwindSafe<futures_lite::future::impl$14::poll::closure_env$0<core::panic::unwind_safe::AssertUnwindSafe<enum2$<bevy_ecs::schedule::executor::multi_threaded::impl$3::spawn_system_task::async_blo
             at /rustc/8460ca823e8367a30dda430efda790588b8c84d3\library\std\src\panicking.rs:483
  20: std::panicking::try::do_catch<core::panic::unwind_safe::AssertUnwindSafe<bevy_ecs::schedule::executor::multi_threaded::impl$3::spawn_system_task::async_block$0::closure_env$0>,tuple$<> >
  21: std::panicking::try<enum2$<core::task::poll::Poll<tuple$<> > >,core::panic::unwind_safe::AssertUnwindSafe<futures_lite::future::impl$14::poll::closure_env$0<core::panic::unwind_safe::AssertUnwindSafe<enum2$<bevy_ecs::schedule::executor::multi_threaded::im
             at /rustc/8460ca823e8367a30dda430efda790588b8c84d3\library\std\src\panicking.rs:447
  22: std::panic::catch_unwind<core::panic::unwind_safe::AssertUnwindSafe<futures_lite::future::impl$14::poll::closure_env$0<core::panic::unwind_safe::AssertUnwindSafe<enum2$<bevy_ecs::schedule::executor::multi_threaded::impl$3::spawn_system_task::async_block_e
             at /rustc/8460ca823e8367a30dda430efda790588b8c84d3\library\std\src\panic.rs:140
  23: futures_lite::future::impl$14::poll<core::panic::unwind_safe::AssertUnwindSafe<enum2$<bevy_ecs::schedule::executor::multi_threaded::impl$3::spawn_system_task::async_block_env$0> > >
             at C:\Users\jonah\.cargo\registry\src\github.com-1ecc6299db9ec823\futures-lite-1.12.0\src\future.rs:626
  24: async_executor::impl$4::spawn::async_block$0<enum2$<core::result::Result<tuple$<>,alloc::boxed::Box<dyn$<core::any::Any,core::marker::Send>,alloc::alloc::Global> > >,futures_lite::future::CatchUnwind<core::panic::unwind_safe::AssertUnwindSafe<enum2$<bevy_
             at C:\Users\jonah\.cargo\registry\src\github.com-1ecc6299db9ec823\async-executor-1.5.0\src\lib.rs:139
  25: async_task::raw::RawTask<enum2$<async_executor::impl$4::spawn::async_block_env$0<enum2$<core::result::Result<tuple$<>,alloc::boxed::Box<dyn$<core::any::Any,core::marker::Send>,alloc::alloc::Global> > >,futures_lite::future::CatchUnwind<core::panic::unwind
             at C:\Users\jonah\.cargo\registry\src\github.com-1ecc6299db9ec823\async-task-4.3.0\src\raw.rs:511
  26: async_task::runnable::Runnable::run
             at C:\Users\jonah\.cargo\registry\src\github.com-1ecc6299db9ec823\async-task-4.3.0\src\runnable.rs:309
  27: async_executor::impl$4::run::async_fn$0::async_block$0<enum2$<core::result::Result<tuple$<>,async_channel::RecvError> >,futures_lite::future::Or<enum2$<bevy_tasks::task_pool::impl$2::new_internal::closure$0::closure$0::closure$0::closure$0::async_block_en
             at C:\Users\jonah\.cargo\registry\src\github.com-1ecc6299db9ec823\async-executor-1.5.0\src\lib.rs:230
  28: futures_lite::future::impl$12::poll<enum2$<core::result::Result<tuple$<>,async_channel::RecvError> >,futures_lite::future::Or<enum2$<bevy_tasks::task_pool::impl$2::new_internal::closure$0::closure$0::closure$0::closure$0::async_block_env$0>,async_channel:
             at C:\Users\jonah\.cargo\registry\src\github.com-1ecc6299db9ec823\futures-lite-1.12.0\src\future.rs:529
  29: async_executor::impl$4::run::async_fn$0<enum2$<core::result::Result<tuple$<>,async_channel::RecvError> >,futures_lite::future::Or<enum2$<bevy_tasks::task_pool::impl$2::new_internal::closure$0::closure$0::closure$0::closure$0::async_block_env$0>,async_chan
             at C:\Users\jonah\.cargo\registry\src\github.com-1ecc6299db9ec823\async-executor-1.5.0\src\lib.rs:237
  30: futures_lite::future::block_on::closure$0<enum2$<core::result::Result<tuple$<>,async_channel::RecvError> >,enum2$<async_executor::impl$4::run::async_fn_env$0<enum2$<core::result::Result<tuple$<>,async_channel::RecvError> >,futures_lite::future::Or<enum2$<
             at C:\Users\jonah\.cargo\registry\src\github.com-1ecc6299db9ec823\futures-lite-1.12.0\src\future.rs:89
  31: std::thread::local::LocalKey<core::cell::RefCell<tuple$<parking::Parker,core::task::wake::Waker> > >::try_with<core::cell::RefCell<tuple$<parking::Parker,core::task::wake::Waker> >,futures_lite::future::block_on::closure_env$0<enum2$<core::result::Result<
             at /rustc/8460ca823e8367a30dda430efda790588b8c84d3\library\std\src\thread\local.rs:446
  32: std::thread::local::LocalKey<core::cell::RefCell<tuple$<parking::Parker,core::task::wake::Waker> > >::with<core::cell::RefCell<tuple$<parking::Parker,core::task::wake::Waker> >,futures_lite::future::block_on::closure_env$0<enum2$<core::result::Result<tupl
             at /rustc/8460ca823e8367a30dda430efda790588b8c84d3\library\std\src\thread\local.rs:422
  33: futures_lite::future::block_on<enum2$<core::result::Result<tuple$<>,async_channel::RecvError> >,enum2$<async_executor::impl$4::run::async_fn_env$0<enum2$<core::result::Result<tuple$<>,async_channel::RecvError> >,futures_lite::future::Or<enum2$<bevy_tasks:
             at C:\Users\jonah\.cargo\registry\src\github.com-1ecc6299db9ec823\futures-lite-1.12.0\src\future.rs:79
  34: bevy_tasks::task_pool::impl$2::new_internal::closure$0::closure$0::closure$0::closure$0
             at E:\Projects\Rust\bevy\crates\bevy_tasks\src\task_pool.rs:171
  35: std::panicking::try::do_call<bevy_tasks::task_pool::impl$2::new_internal::closure$0::closure$0::closure$0::closure_env$0,enum2$<core::result::Result<tuple$<>,async_channel::RecvError> > >
             at /rustc/8460ca823e8367a30dda430efda790588b8c84d3\library\std\src\panicking.rs:483
  36: std::panicking::try::do_catch<core::panic::unwind_safe::AssertUnwindSafe<std::thread::impl$0::spawn_unchecked_::closure$1::closure_env$0<bevy_tasks::task_pool::impl$2::new_internal::closure$0::closure_env$0,tuple$<> > >,tuple$<> >
  37: std::panicking::try<enum2$<core::result::Result<tuple$<>,async_channel::RecvError> >,bevy_tasks::task_pool::impl$2::new_internal::closure$0::closure$0::closure$0::closure_env$0>
             at /rustc/8460ca823e8367a30dda430efda790588b8c84d3\library\std\src\panicking.rs:447
  38: std::panic::catch_unwind<bevy_tasks::task_pool::impl$2::new_internal::closure$0::closure$0::closure$0::closure_env$0,enum2$<core::result::Result<tuple$<>,async_channel::RecvError> > >
             at /rustc/8460ca823e8367a30dda430efda790588b8c84d3\library\std\src\panic.rs:140
  39: bevy_tasks::task_pool::impl$2::new_internal::closure$0::closure$0::closure$0
             at E:\Projects\Rust\bevy\crates\bevy_tasks\src\task_pool.rs:165
  40: std::thread::local::LocalKey<async_executor::LocalExecutor>::try_with<async_executor::LocalExecutor,bevy_tasks::task_pool::impl$2::new_internal::closure$0::closure$0::closure_env$0,tuple$<> >
             at /rustc/8460ca823e8367a30dda430efda790588b8c84d3\library\std\src\thread\local.rs:446
  41: std::thread::local::LocalKey<async_executor::LocalExecutor>::with<async_executor::LocalExecutor,bevy_tasks::task_pool::impl$2::new_internal::closure$0::closure$0::closure_env$0,tuple$<> >
             at /rustc/8460ca823e8367a30dda430efda790588b8c84d3\library\std\src\thread\local.rs:422
  42: bevy_tasks::task_pool::impl$2::new_internal::closure$0::closure$0
             at E:\Projects\Rust\bevy\crates\bevy_tasks\src\task_pool.rs:158
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
Encountered a panic in system `bevy_test::panicking_system_2`!
Encountered a panic in system `bevy_app::main_schedule::Main::run_main`!
error: process didn't exit successfully: `target\debug\bevy_test.exe` (exit code: 101)

@mockersf
Copy link
Member

mockersf commented Mar 30, 2023

Yeah, but IDK if there is any thing we can do there. I think it's more a problem with panic! printing both at once. Edit: it should be a rare occurence that two threads panic at the exact same time.

Yup, definitely not something to solve here.

At least the stacktraces looks good

@JonahPlusPlus
Copy link
Contributor Author

Oh yeah, I guess I should be using error! instead of println!. I'll change that later.

@JonahPlusPlus
Copy link
Contributor Author

JonahPlusPlus commented Mar 30, 2023

Actually, I changed my mind. error! is pretty noisy, so eprintln!. You'll want to record stderr anyways, since that is where the panic message goes to, so printing to stderr keeps everything in the same spot.

Edit: What it would look like with error!:

   Compiling bevy_test v0.1.0 (E:\Projects\Rust\bevy_test)
    Finished dev [unoptimized + debuginfo] target(s) in 58.43s
     Running `target\debug\bevy_test.exe`
2023-03-30T23:50:14.825058Z  INFO bevy_diagnostic::system_information_diagnostics_plugin::internal: SystemInfo { os: "Windows 10 Pro", kernel: "19044", cpu: "AMD Ryzen 5 2600 Six-Core Processor", core_count: "6", memory: "15.9 GiB" }
thread 'Compute Task Pool (1)' panicked at 'oooh scary', src\main.rs:11:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
2023-03-30T23:50:14.831625Z ERROR bevy_ecs::schedule::executor::multi_threaded: Encountered a panic in system `bevy_test::panicking_system`!
2023-03-30T23:50:14.831895Z ERROR bevy_ecs::schedule::executor::single_threaded: Encountered a panic in system `bevy_app::main_schedule::Main::run_main`!

@alice-i-cecile
Copy link
Member

And error won't work with tracing turned off IIRC :)

@JonahPlusPlus
Copy link
Contributor Author

JonahPlusPlus commented Mar 31, 2023

No, the trace feature doesn't change any dependencies, it just toggles spans. But now that you mention it, I guess it wouldn't work if the bevy_log plugin isn't added.

Edit: Oh, maybe that's what you meant?

@alice-i-cecile
Copy link
Member

Right, that was the edge case I was half-remembering :)

let task = self.executor.spawn(f).fallible();
let task = self
.executor
.spawn(AssertUnwindSafe(f).catch_unwind())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we can guarantee that the passed in future is UnwindSafe.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going to have to look more into it, since we do execute other systems after one fails, but it seems other projects ignore unwind safety (I may have misinterpreted it though, I've got to go back to work).

Copy link
Contributor

@hymm hymm Mar 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Send + 'static pretty much implies unwind safe.

In our case the futures aren't 'static when spawning on the scope.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, so I guess the thing to do would be make it so the spawn functions can only take unwind safe futures and then assure that safety from bevy_ecs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I took a more in depth look at this and I think unwind safety doesn't matter here. Sure, we are catching the unwinds, but we aren't restarting tasks or returning the result, instead the panic is being propagated instead of creating a new panic. The real problem is in bevy_ecs, where catching an unwind previously led to a panic, now still runs the systems. In order to guarantee that broken invariants aren't observed, the mutable accesses of the panicked system need to be marked, and any system that depends on any of the marked accesses need to be skipped.

@JonahPlusPlus
Copy link
Contributor Author

JonahPlusPlus commented Apr 2, 2023

Hmmm, so this has already become pretty complex, and it seems there is even more work to do. The major problem is unwind safety. In order to run systems after the panic, we have to account for any corruption that may have occurred. This can be done by skipping systems that depend on affected accesses. However, this severely limits the benefits of running systems afterwards. For example, if the panicking system is exclusive, then no system could run. And a future system is exclusive, it can't run either.

Anyways, my point is that this is a bad way to handle a graceful shutdown. A better alternative might be providing a panic handler in the System trait that is called when a system panics that can handle cleanup (this is unnecessary in systems that are self contained, just remove the panics, but necessary for systems that expect future systems to do cleanup and are interrupted by another panicking system) and let only the running systems finish.

So I'm going to change this to only wait for running systems to stop. I'll leave figuring out graceful shutdowns to someone who has more experience with the ECS internals.

@JonahPlusPlus
Copy link
Contributor Author

There, I think that fixes everything. Currently running systems are allowed to finish, but the executor won't spawn any more systems. Future work will have to be done to allow for advanced clean up. Maybe that might even look like what I was attempting, but with some method of insuring unwind safety (unsafe trait UnwindSafeResource {}?).

Copy link
Member

@alice-i-cecile alice-i-cecile left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a solid improvement over the current state of things, and this is critical work. Let's merge this IMO.

@mockersf
Copy link
Member

Looks good. Let's merge and catch edge casses on main if any

@alice-i-cecile alice-i-cecile added this pull request to the merge queue Apr 12, 2023
#[cfg(feature = "trace")]
system_span.exit();
if let Err(payload) = res {
eprintln!("Encountered a panic in system `{}`!", &*system.name());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a bit too late now, but in a future PR something like this could make the error stand out a bit more

Suggested change
eprintln!("Encountered a panic in system `{}`!", &*system.name());
eprintln!("\x1b[91mERROR\x1b[0m: Encountered a panic in system `{}`!", &*system.name());

This will make the ERROR red and look like this:
image

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this supported in all terminals on all platforms?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know it works on windows at least and I would be surprised if it didn't work on linux. but I don't have a way to confirm it right now. There are crates that can handle that more gracefully though, just not sure if adding a crate just for that is required, although, tracing probably already uses one of them so we could just reuse that one since it's already in the dep tree.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And when logs are redirected to a file? I don't know of a single way to have colors...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could use error! macro to have colors in terminal and normal output if colors aren't supported.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could use error!

error! wasn't used because of verbosity concerns. I don't personally agree, but I suggested that approach as an alternative. I think we should instead change the default format of logs if the verbosity is a concern instead of avoiding it, but that's out of scope for here.

And when logs are redirected to a file

True, I didn't really consider that. At least it would just be a couple of weird characters at the end of the file, but it's indeed not ideal.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should instead change the default format of logs if the verbosity is a concern instead of avoiding it,

Makes sense to me.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know of a single way to have colors...

Well, there's error!() that can deal with it correctly 😉. So it's a way, but like mentioned it was purposefully not used.

Copy link
Member

@mockersf mockersf Apr 12, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, there's error!() that can deal with it correctly

I meant by adding control characters. As far as I know the tracing library is using environment detection.

error! wasn't used because of verbosity concerns

Oh I thought it was because bevy_ecs can be used without logs and tracing enabled

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I was basing it on this comment #8219 (comment) but alice mentioned just after that that it won't work with tracing disabled.

Not sure what the best solution is, maybe we could just use a feature flag for tracing and if it's disabled just use the current log with no color?

It jus seems unfortunate to not use tracing where possible. For example, if someone sets up a tracing layer to output to a file they won't get those logs currently, which really isn't ideal.

Merged via the queue into bevyengine:main with commit 55e9ab7 Apr 12, 2023
@JonahPlusPlus JonahPlusPlus deleted the better_panics branch April 17, 2023 21:11
github-merge-queue bot pushed a commit that referenced this pull request Jan 21, 2024
…11455)

# Objective

#8219 changed the target type of a `transmute` without changing the one
transmuting from ([see the relevant
diff](55e9ab7#diff-11413fb2eeba97978379d325353d32aa76eefd0af0c8e9b50b7f394ddfda7a26R351-R355)),
making them incompatible. This PR fixes this by changing the initial
type to match the target one (modulo lifetimes).

## Solution

Change the type to be transmuted from to match the one transmuting into
(modulo lifetimes)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-ECS Entities, components, systems, and events C-Usability A targeted quality-of-life change that makes Bevy easier to use
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Panics are getting hard to understand
6 participants