Adding support for shutdown upon Ctrl+C on Windows for LLMP (#1704)
* Adding support for shutdown upon Ctrl+C on Windows for LLMP * PR comments and clippy suggestions addressed * Enable CI for PR branches and manually triggered CI * Removed an empty line that broke compilation on some platforms * Trying to fix nostd compilation * Trying to fix nostd compilation for nightly toolchain * Removing use that is unused on some platforms * Trying to fix build on the nightly toolchain * Trying to fix build on the nightly toolchain, take 2 * Unifying LlmpShutdownSignalHandler * Fmt fix * Making the handler pub(crate) * Nightly toolchain fmt fixes --------- Co-authored-by: Dongjia "toka" Zhang <tokazerkje@outlook.com>
This commit is contained in:
parent
686d29a3cb
commit
b336411516
4
.github/workflows/build_and_test.yml
vendored
4
.github/workflows/build_and_test.yml
vendored
@ -2,10 +2,10 @@ name: build and test
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ main ]
|
branches: [ main, 'pr/**' ]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ main ]
|
branches: [ main ]
|
||||||
|
workflow_dispatch:
|
||||||
env:
|
env:
|
||||||
CARGO_TERM_COLOR: always
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
|
@ -121,7 +121,7 @@ libc = "0.2" # For (*nix) libc
|
|||||||
uds = { version = "0.4", optional = true, default-features = false }
|
uds = { version = "0.4", optional = true, default-features = false }
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
windows = { version = "0.51.1", features = ["Win32_Foundation", "Win32_System_Threading", "Win32_System_Diagnostics_Debug", "Win32_System_Kernel", "Win32_System_Memory", "Win32_Security", "Win32_System_SystemInformation"] }
|
windows = { version = "0.51.1", features = ["Win32_Foundation", "Win32_System_Threading", "Win32_System_Diagnostics_Debug", "Win32_System_Kernel", "Win32_System_Memory", "Win32_Security", "Win32_System_SystemInformation", "Win32_System_Console"] }
|
||||||
|
|
||||||
[target.'cfg(windows)'.build-dependencies]
|
[target.'cfg(windows)'.build-dependencies]
|
||||||
windows = "0.51.1"
|
windows = "0.51.1"
|
||||||
|
@ -100,6 +100,8 @@ use crate::current_time;
|
|||||||
use crate::os::unix_signals::setup_signal_handler;
|
use crate::os::unix_signals::setup_signal_handler;
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use crate::os::unix_signals::{siginfo_t, ucontext_t, Handler, Signal};
|
use crate::os::unix_signals::{siginfo_t, ucontext_t, Handler, Signal};
|
||||||
|
#[cfg(all(windows, feature = "std"))]
|
||||||
|
use crate::os::windows_exceptions::{setup_ctrl_handler, CtrlHandler};
|
||||||
use crate::{
|
use crate::{
|
||||||
shmem::{ShMem, ShMemDescription, ShMemId, ShMemProvider},
|
shmem::{ShMem, ShMemDescription, ShMemId, ShMemProvider},
|
||||||
ClientId, Error,
|
ClientId, Error,
|
||||||
@ -175,7 +177,7 @@ const EOP_MSG_SIZE: usize =
|
|||||||
const LLMP_PAGE_HEADER_LEN: usize = size_of::<LlmpPage>();
|
const LLMP_PAGE_HEADER_LEN: usize = size_of::<LlmpPage>();
|
||||||
|
|
||||||
/// The llmp broker registers a signal handler for cleanups on `SIGINT`.
|
/// The llmp broker registers a signal handler for cleanups on `SIGINT`.
|
||||||
#[cfg(unix)]
|
#[cfg(any(unix, all(windows, feature = "std")))]
|
||||||
static mut LLMP_SIGHANDLER_STATE: LlmpShutdownSignalHandler = LlmpShutdownSignalHandler {
|
static mut LLMP_SIGHANDLER_STATE: LlmpShutdownSignalHandler = LlmpShutdownSignalHandler {
|
||||||
shutting_down: false,
|
shutting_down: false,
|
||||||
};
|
};
|
||||||
@ -1962,7 +1964,9 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A signal handler for the [`LlmpBroker`].
|
/// A signal handler for the [`LlmpBroker`].
|
||||||
#[cfg(unix)]
|
/// On unix, it handles signals
|
||||||
|
/// On Windows - control signals (e.g., CTRL+C)
|
||||||
|
#[cfg(any(unix, all(windows, feature = "std")))]
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct LlmpShutdownSignalHandler {
|
pub struct LlmpShutdownSignalHandler {
|
||||||
shutting_down: bool,
|
shutting_down: bool,
|
||||||
@ -1986,6 +1990,18 @@ impl Handler for LlmpShutdownSignalHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(all(windows, feature = "std"))]
|
||||||
|
impl CtrlHandler for LlmpShutdownSignalHandler {
|
||||||
|
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||||
|
fn handle(&mut self, ctrl_type: u32) -> bool {
|
||||||
|
log::info!("LLMP: Received shutdown signal, ctrl_type {:?}", ctrl_type);
|
||||||
|
unsafe {
|
||||||
|
ptr::write_volatile(&mut self.shutting_down, true);
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The broker forwards all messages to its own bus-like broadcast map.
|
/// The broker forwards all messages to its own bus-like broadcast map.
|
||||||
/// It may intercept messages passing through.
|
/// It may intercept messages passing through.
|
||||||
impl<SP> LlmpBroker<SP>
|
impl<SP> LlmpBroker<SP>
|
||||||
@ -2242,15 +2258,37 @@ where
|
|||||||
|
|
||||||
/// Internal function, returns true when shuttdown is requested by a `SIGINT` signal
|
/// Internal function, returns true when shuttdown is requested by a `SIGINT` signal
|
||||||
#[inline]
|
#[inline]
|
||||||
#[cfg(unix)]
|
#[cfg(any(unix, all(windows, feature = "std")))]
|
||||||
#[allow(clippy::unused_self)]
|
#[allow(clippy::unused_self)]
|
||||||
fn is_shutting_down(&self) -> bool {
|
fn is_shutting_down(&self) -> bool {
|
||||||
unsafe { ptr::read_volatile(ptr::addr_of!(LLMP_SIGHANDLER_STATE.shutting_down)) }
|
unsafe { ptr::read_volatile(ptr::addr_of!(LLMP_SIGHANDLER_STATE.shutting_down)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(any(all(unix, not(miri)), all(windows, feature = "std")))]
|
||||||
|
fn setup_handlers() {
|
||||||
|
#[cfg(all(unix, not(miri)))]
|
||||||
|
if let Err(e) = unsafe { setup_signal_handler(&mut LLMP_SIGHANDLER_STATE) } {
|
||||||
|
// We can live without a proper ctrl+c signal handler - Ignore.
|
||||||
|
log::info!("Failed to setup signal handlers: {e}");
|
||||||
|
} else {
|
||||||
|
log::info!("Successfully setup signal handlers");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(windows, feature = "std"))]
|
||||||
|
if let Err(e) = unsafe { setup_ctrl_handler(&mut LLMP_SIGHANDLER_STATE) } {
|
||||||
|
// We can live without a proper ctrl+c signal handler - Ignore.
|
||||||
|
log::info!("Failed to setup control handlers: {e}");
|
||||||
|
} else {
|
||||||
|
log::info!(
|
||||||
|
"{}: Broker successfully setup control handlers",
|
||||||
|
std::process::id().to_string()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Always returns true on platforms, where no shutdown signal handlers are supported
|
/// Always returns true on platforms, where no shutdown signal handlers are supported
|
||||||
#[inline]
|
#[inline]
|
||||||
#[cfg(not(unix))]
|
#[cfg(not(any(unix, all(windows, feature = "std"))))]
|
||||||
#[allow(clippy::unused_self)]
|
#[allow(clippy::unused_self)]
|
||||||
fn is_shutting_down(&self) -> bool {
|
fn is_shutting_down(&self) -> bool {
|
||||||
false
|
false
|
||||||
@ -2280,11 +2318,8 @@ where
|
|||||||
{
|
{
|
||||||
use super::current_milliseconds;
|
use super::current_milliseconds;
|
||||||
|
|
||||||
#[cfg(all(unix, not(miri)))]
|
#[cfg(any(all(unix, not(miri)), all(windows, feature = "std")))]
|
||||||
if let Err(_e) = unsafe { setup_signal_handler(&mut LLMP_SIGHANDLER_STATE) } {
|
Self::setup_handlers();
|
||||||
// We can live without a proper ctrl+c signal handler. Print and ignore.
|
|
||||||
log::info!("Failed to setup signal handlers: {_e}");
|
|
||||||
}
|
|
||||||
|
|
||||||
let timeout = timeout.as_millis() as u64;
|
let timeout = timeout.as_millis() as u64;
|
||||||
let mut end_time = current_milliseconds() + timeout;
|
let mut end_time = current_milliseconds() + timeout;
|
||||||
@ -2344,11 +2379,8 @@ where
|
|||||||
where
|
where
|
||||||
F: FnMut(ClientId, Tag, Flags, &[u8]) -> Result<LlmpMsgHookResult, Error>,
|
F: FnMut(ClientId, Tag, Flags, &[u8]) -> Result<LlmpMsgHookResult, Error>,
|
||||||
{
|
{
|
||||||
#[cfg(all(unix, not(miri)))]
|
#[cfg(any(all(unix, not(miri)), all(windows, feature = "std")))]
|
||||||
if let Err(_e) = unsafe { setup_signal_handler(&mut LLMP_SIGHANDLER_STATE) } {
|
Self::setup_handlers();
|
||||||
// We can live without a proper ctrl+c signal handler. Print and ignore.
|
|
||||||
log::info!("Failed to setup signal handlers: {_e}");
|
|
||||||
}
|
|
||||||
|
|
||||||
while !self.is_shutting_down() {
|
while !self.is_shutting_down() {
|
||||||
self.once(on_new_msg)
|
self.once(on_new_msg)
|
||||||
|
@ -11,10 +11,12 @@ use core::{
|
|||||||
};
|
};
|
||||||
use std::os::raw::{c_long, c_void};
|
use std::os::raw::{c_long, c_void};
|
||||||
|
|
||||||
|
use log::info;
|
||||||
use num_enum::TryFromPrimitive;
|
use num_enum::TryFromPrimitive;
|
||||||
pub use windows::Win32::{
|
pub use windows::Win32::{
|
||||||
Foundation::NTSTATUS,
|
Foundation::{BOOL, NTSTATUS},
|
||||||
System::{
|
System::{
|
||||||
|
Console::{SetConsoleCtrlHandler, CTRL_BREAK_EVENT, CTRL_C_EVENT, PHANDLER_ROUTINE},
|
||||||
Diagnostics::Debug::{
|
Diagnostics::Debug::{
|
||||||
AddVectoredExceptionHandler, UnhandledExceptionFilter, EXCEPTION_POINTERS,
|
AddVectoredExceptionHandler, UnhandledExceptionFilter, EXCEPTION_POINTERS,
|
||||||
},
|
},
|
||||||
@ -321,11 +323,21 @@ unsafe fn internal_handle_exception(
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
match &EXCEPTION_HANDLERS[index] {
|
match &EXCEPTION_HANDLERS[index] {
|
||||||
Some(handler_holder) => {
|
Some(handler_holder) => {
|
||||||
|
info!(
|
||||||
|
"{:?}: Handling exception {}",
|
||||||
|
std::process::id(),
|
||||||
|
exception_code
|
||||||
|
);
|
||||||
let handler = &mut **handler_holder.handler.get();
|
let handler = &mut **handler_holder.handler.get();
|
||||||
handler.handle(exception_code, exception_pointers);
|
handler.handle(exception_code, exception_pointers);
|
||||||
EXCEPTION_CONTINUE_EXECUTION
|
EXCEPTION_CONTINUE_EXECUTION
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
|
info!(
|
||||||
|
"{:?}: No handler for exception {}",
|
||||||
|
std::process::id(),
|
||||||
|
exception_code
|
||||||
|
);
|
||||||
// Go to Default one
|
// Go to Default one
|
||||||
let handler_holder = &EXCEPTION_HANDLERS[EXCEPTION_HANDLERS_SIZE - 1]
|
let handler_holder = &EXCEPTION_HANDLERS[EXCEPTION_HANDLERS_SIZE - 1]
|
||||||
.as_ref()
|
.as_ref()
|
||||||
@ -351,7 +363,7 @@ pub unsafe extern "system" fn handle_exception(
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.ExceptionCode;
|
.ExceptionCode;
|
||||||
let exception_code = ExceptionCode::try_from(code.0).unwrap();
|
let exception_code = ExceptionCode::try_from(code.0).unwrap();
|
||||||
// log::info!("Received exception; code: {}", exception_code);
|
log::info!("Received exception; code: {}", exception_code);
|
||||||
internal_handle_exception(exception_code, exception_pointers)
|
internal_handle_exception(exception_code, exception_pointers)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -406,3 +418,59 @@ pub unsafe fn setup_exception_handler<T: 'static + Handler>(handler: &mut T) ->
|
|||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
pub(crate) trait CtrlHandler {
|
||||||
|
/// Handle an exception
|
||||||
|
fn handle(&mut self, ctrl_type: u32) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CtrlHandlerHolder {
|
||||||
|
handler: UnsafeCell<*mut dyn CtrlHandler>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Keep track of which handler is registered for which exception
|
||||||
|
static mut CTRL_HANDLER: Option<CtrlHandlerHolder> = None;
|
||||||
|
|
||||||
|
/// Set `ConsoleCtrlHandler` to catch Ctrl-C
|
||||||
|
/// # Safety
|
||||||
|
/// Same safety considerations as in `setup_exception_handler`
|
||||||
|
pub(crate) unsafe fn setup_ctrl_handler<T: 'static + CtrlHandler>(
|
||||||
|
handler: &mut T,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
write_volatile(
|
||||||
|
&mut CTRL_HANDLER,
|
||||||
|
Some(CtrlHandlerHolder {
|
||||||
|
handler: UnsafeCell::new(handler as *mut dyn CtrlHandler),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
compiler_fence(Ordering::SeqCst);
|
||||||
|
|
||||||
|
// Log the result of SetConsoleCtrlHandler
|
||||||
|
let result = SetConsoleCtrlHandler(Some(ctrl_handler), true);
|
||||||
|
match result {
|
||||||
|
Ok(()) => {
|
||||||
|
info!("SetConsoleCtrlHandler succeeded");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
info!("SetConsoleCtrlHandler failed");
|
||||||
|
Err(Error::from(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "system" fn ctrl_handler(ctrl_type: u32) -> BOOL {
|
||||||
|
match &CTRL_HANDLER {
|
||||||
|
Some(handler_holder) => {
|
||||||
|
info!("{:?}: Handling ctrl {}", std::process::id(), ctrl_type);
|
||||||
|
let handler = &mut *handler_holder.handler.get();
|
||||||
|
if let Some(ctrl_handler) = handler.as_mut() {
|
||||||
|
(*ctrl_handler).handle(ctrl_type).into()
|
||||||
|
} else {
|
||||||
|
false.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => false.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user