Skip to content

Commit 4572a1d

Browse files
authored
Shutdown immediately if ctrl+c pressed again (#22593)
# Objective Prevent the program from hanging if user code that runs on shutdown is buggy. ## Solution Update the `TerminalCtrlcHandlerPlugin` to exit immediately if `ctrl-c` is pressed again. ## Testing I ran this program and verified that it stopped the second time I pressed `ctrl-c`. ```rust use bevy::{app::TerminalCtrlCHandlerPlugin, log::LogPlugin, prelude::*}; fn main() { App::new() .add_plugins((TerminalCtrlCHandlerPlugin, LogPlugin::default())) .add_systems(Update, misbehaving_system) .run(); } #[expect(clippy::empty_loop, reason = "testing system that hangs")] fn misbehaving_system() { loop {} } ``` To automate this it needs to be run in a separate process with a launcher that kills it if it times out. I'm not sure if that is desired or how that'd fit into bevy's testing setup.
1 parent 8f79dbe commit 4572a1d

File tree

1 file changed

+14
-6
lines changed

1 file changed

+14
-6
lines changed

crates/bevy_app/src/terminal_ctrl_c_handler.rs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use core::sync::atomic::{AtomicBool, Ordering};
1+
use core::sync::atomic::{AtomicU8, Ordering};
22

33
use bevy_ecs::message::MessageWriter;
44

@@ -7,7 +7,7 @@ use crate::{App, AppExit, Plugin, Update};
77
pub use ctrlc;
88

99
/// Indicates that all [`App`]'s should exit.
10-
static SHOULD_EXIT: AtomicBool = AtomicBool::new(false);
10+
static SHOULD_EXIT: AtomicU8 = AtomicU8::new(0);
1111

1212
/// Gracefully handles `Ctrl+C` by emitting a [`AppExit`] event. This plugin is part of the `DefaultPlugins`.
1313
///
@@ -42,17 +42,25 @@ static SHOULD_EXIT: AtomicBool = AtomicBool::new(false);
4242
pub struct TerminalCtrlCHandlerPlugin;
4343

4444
impl TerminalCtrlCHandlerPlugin {
45-
/// Sends the [`AppExit`] event to all apps using this plugin to make them gracefully exit.
45+
/// When called the first time, it sends the [`AppExit`] event to all apps using
46+
/// this plugin to make them gracefully exit.
47+
///
48+
/// If called more than once, it exits immediately.
4649
pub fn gracefully_exit() {
47-
SHOULD_EXIT.store(true, Ordering::Relaxed);
50+
if SHOULD_EXIT.fetch_add(1, Ordering::SeqCst) > 0 {
51+
log::error!("Received more than one ctrl+c. Skipping graceful shutdown.");
52+
std::process::exit(Self::EXIT_CODE.into());
53+
};
4854
}
4955

5056
/// Sends a [`AppExit`] event when the user presses `Ctrl+C` on the terminal.
5157
pub fn exit_on_flag(mut app_exit_writer: MessageWriter<AppExit>) {
52-
if SHOULD_EXIT.load(Ordering::Relaxed) {
53-
app_exit_writer.write(AppExit::from_code(130));
58+
if SHOULD_EXIT.load(Ordering::Relaxed) > 0 {
59+
app_exit_writer.write(AppExit::from_code(Self::EXIT_CODE));
5460
}
5561
}
62+
63+
const EXIT_CODE: u8 = 130;
5664
}
5765

5866
impl Plugin for TerminalCtrlCHandlerPlugin {

0 commit comments

Comments
 (0)