Skip to content

Commit

Permalink
Introduce try_set_handler
Browse files Browse the repository at this point in the history
Revert set_handler to overwrite signal handlers, introduce
try_set_handler that does not overwrite existing handlers.
  • Loading branch information
Detegr committed May 30, 2023
1 parent 941c323 commit 89e7ed0
Show file tree
Hide file tree
Showing 5 changed files with 36 additions and 19 deletions.
39 changes: 30 additions & 9 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,30 +73,51 @@ static INIT_LOCK: Mutex<()> = Mutex::new(());
/// ```
///
/// # Warning
/// On Unix, the handler registration for `SIGINT`, (`SIGTERM` and `SIGHUP` if termination feature is enabled) or `SA_SIGINFO`
/// posix signal handlers will fail if a signal handler is already present. On Windows, multiple handler routines are allowed,
/// but they are called on a last-registered, first-called basis until the signal is handled.
/// On Unix, the handler registration for `SIGINT`, (`SIGTERM` and `SIGHUP` if termination feature
/// is enabled) or `SA_SIGINFO` posix signal handlers will be overwritten. On Windows, multiple
/// handler routines are allowed, but they are called on a last-registered, first-called basis
/// until the signal is handled.
///
/// ctrlc::try_set_handler will error (on Unix) if another signal handler exists for the same
/// signal(s) that ctrlc is trying to attach the handler to.
///
/// On Unix, signal dispositions and signal handlers are inherited by child processes created via
/// `fork(2)` on, but not by child processes created via `execve(2)`.
/// Signal handlers are not inherited on Windows.
///
/// # Errors
/// Will return an error if another `ctrlc::set_handler()` handler exists or if a
/// system error occurred while setting the handler.
/// Will return an error if a system error occurred while setting the handler.
///
/// # Panics
/// Any panic in the handler will not be caught and will cause the signal handler thread to stop.
///
pub fn set_handler<F>(user_handler: F) -> Result<(), Error>
where
F: FnMut() + 'static + Send,
{
init_and_set_handler(user_handler, true)
}

/// The same as ctrlc::set_handler but errors if a handler already exists for the signal(s).
///
/// # Errors
/// Will return an error if another handler exists or if a system error occurred while setting the
/// handler.
pub fn try_set_handler<F>(user_handler: F) -> Result<(), Error>
where
F: FnMut() + 'static + Send,
{
init_and_set_handler(user_handler, false)
}

fn init_and_set_handler<F>(user_handler: F, overwrite: bool) -> Result<(), Error>
where
F: FnMut() + 'static + Send,
{
if !INIT.load(Ordering::Acquire) {
let _guard = INIT_LOCK.lock().unwrap();

if !INIT.load(Ordering::Relaxed) {
set_handler_inner(user_handler)?;
set_handler_inner(user_handler, overwrite)?;
INIT.store(true, Ordering::Release);
return Ok(());
}
Expand All @@ -105,12 +126,12 @@ where
Err(Error::MultipleHandlers)
}

fn set_handler_inner<F>(mut user_handler: F) -> Result<(), Error>
fn set_handler_inner<F>(mut user_handler: F, overwrite: bool) -> Result<(), Error>
where
F: FnMut() + 'static + Send,
{
unsafe {
match platform::init_os_handler() {
match platform::init_os_handler(overwrite) {
Ok(_) => {}
Err(err) => {
return Err(err.into());
Expand Down
8 changes: 4 additions & 4 deletions src/platform/unix/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ fn pipe2(flags: nix::fcntl::OFlag) -> nix::Result<(RawFd, RawFd)> {
/// Will return an error if a system error occurred.
///
#[inline]
pub unsafe fn init_os_handler() -> Result<(), Error> {
pub unsafe fn init_os_handler(overwrite: bool) -> Result<(), Error> {
use nix::fcntl;
use nix::sys::signal;

Expand Down Expand Up @@ -113,7 +113,7 @@ pub unsafe fn init_os_handler() -> Result<(), Error> {
Ok(old) => old,
Err(e) => return Err(close_pipe(e)),
};
if sigint_old.handler() != signal::SigHandler::SigDfl {
if !overwrite && sigint_old.handler() != signal::SigHandler::SigDfl {
signal::sigaction(signal::Signal::SIGINT, &sigint_old).unwrap();
return Err(close_pipe(nix::Error::EEXIST));
}
Expand All @@ -127,7 +127,7 @@ pub unsafe fn init_os_handler() -> Result<(), Error> {
return Err(close_pipe(e));
}
};
if sigterm_old.handler() != signal::SigHandler::SigDfl {
if !overwrite && sigterm_old.handler() != signal::SigHandler::SigDfl {
signal::sigaction(signal::Signal::SIGINT, &sigint_old).unwrap();
signal::sigaction(signal::Signal::SIGTERM, &sigterm_old).unwrap();
return Err(close_pipe(nix::Error::EEXIST));
Expand All @@ -140,7 +140,7 @@ pub unsafe fn init_os_handler() -> Result<(), Error> {
return Err(close_pipe(e));
}
};
if sighup_old.handler() != signal::SigHandler::SigDfl {
if !overwrite && sighup_old.handler() != signal::SigHandler::SigDfl {
signal::sigaction(signal::Signal::SIGINT, &sigint_old).unwrap();
signal::sigaction(signal::Signal::SIGTERM, &sigterm_old).unwrap();
signal::sigaction(signal::Signal::SIGHUP, &sighup_old).unwrap();
Expand Down
2 changes: 1 addition & 1 deletion src/platform/windows/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ unsafe extern "system" fn os_handler(_: u32) -> BOOL {
/// Will return an error if a system error occurred.
///
#[inline]
pub unsafe fn init_os_handler() -> Result<(), Error> {
pub unsafe fn init_os_handler(_overwrite: bool) -> Result<(), Error> {
SEMAPHORE = CreateSemaphoreA(ptr::null_mut(), 0, MAX_SEM_COUNT, ptr::null());
if SEMAPHORE == 0 {
return Err(io::Error::last_os_error());
Expand Down
2 changes: 1 addition & 1 deletion tests/main/issue_97.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use test_signal_hook::run_signal_hook;

fn expect_multiple_handlers() {
#[cfg(not(windows))]
match ctrlc::set_handler(|| {}) {
match ctrlc::try_set_handler(|| {}) {
Err(ctrlc::Error::MultipleHandlers) => {}
_ => panic!("Expected Error::MultipleHandlers"),
}
Expand Down
4 changes: 0 additions & 4 deletions tests/main/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@
mod harness;
use harness::{platform, run_harness};

mod test_signal_hook;
use test_signal_hook::run_signal_hook;

use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
Expand Down Expand Up @@ -42,7 +39,6 @@ fn test_set_handler() {

fn tests() {
run_tests!(test_set_handler);
run_tests!(run_signal_hook);
}

fn main() {
Expand Down

0 comments on commit 89e7ed0

Please sign in to comment.