Skip to content

[#436] Split up the sig handler into non fatal and fatal signals #448

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

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 93 additions & 32 deletions iceoryx2-bb/posix/src/signal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
//!
//! fn some_task() {}
//!
//! while SignalHandler::last_signal() != Some(FetchableSignal::Terminate) {
//! while SignalHandler::last_signal() != Some(NonFatalFetchableSignal::Terminate) {
//! some_task();
//! }
//! ```
Expand All @@ -50,7 +50,7 @@
//! ```no_run
//! use iceoryx2_bb_posix::signal::*;
//!
//! SignalHandler::wait_for_signal(FetchableSignal::Terminate);
//! SignalHandler::wait_for_signal(NonFatalFetchableSignal::Terminate);
//! ```
use std::{
fmt::{Debug, Display},
Expand All @@ -75,32 +75,54 @@ use tiny_fn::tiny_fn;

macro_rules! define_signals {
{fetchable: $($entry:ident = $nn:ident::$value:ident),*
fatal_fetchable: $($fatal_entry:ident = $fatal_nn:ident::$fatal_value:ident),*
unknown_translates_to: $unknown_entry:ident = $unknown_nn:ident::$unknown_value:ident
unfetchable: $($uentry:ident = $unn:ident::$uvalue:ident),*
} => {
/// Represents signals which cannot be fetched by a signal handler.
#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq, Sequence)]
#[repr(i32)]
pub enum UnfetchableSignal {
$($uentry = $unn::$uvalue),*,
}

enum_gen! {
/// Represents signals that can be fetched and are non-fatal, meaning that they do not
/// indicate that the process is somehow corrupted. For example: `SIGUSR1` or `SIGINT`.
#[derive(Sequence)]
NonFatalFetchableSignal
unknown_translates_to:
$unknown_entry = $unknown_nn::$unknown_value
entry:
$($entry = $nn::$value),*
}

/// Represents signals that can be fetched and are fatal, meaning that they
/// indicate that the process is somehow corrupted and should terminate.
#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq, Sequence)]
#[repr(i32)]
pub enum FatalFetchableSignal {
Copy link
Contributor

Choose a reason for hiding this comment

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

Nothing to change, but just out of curiosity - why the distinction of being Fetchable ? What is a NonFetchableSignal ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For NonFetchableSignal you cannot register a custom callback via sigaction. For instance SIGKILL does not send anything to the process but the OS directly kills the process without informing it.

$($fatal_entry = $fatal_nn::$value),*,
}

enum_gen! {
/// Represents all signals which can be fetched by a signal handler.
#[derive(Sequence)]
FetchableSignal
unknown_translates_to:
$unknown_entry = $unknown_nn::$unknown_value
entry:
$($entry = $nn::$value),*
$($entry = $nn::$value),*,
$($fatal_entry = $fatal_nn::$fatal_value),*
}

/// Represents all known POSIX signals
#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq, Sequence)]
#[repr(i32)]
pub enum Signal {
$($entry = $nn::$value),*,
$($uentry = $unn::$uvalue),*,
$($fatal_entry = $fatal_nn::$fatal_value),*,
$unknown_entry = $unknown_nn::$unknown_value
}

Expand All @@ -112,10 +134,45 @@ macro_rules! define_signals {
}
}

impl From<FatalFetchableSignal> for FetchableSignal {
fn from(v: FatalFetchableSignal) -> Self {
match v {
$(FatalFetchableSignal::$fatal_entry => FetchableSignal::$fatal_entry),*
}
}
}

impl From<FatalFetchableSignal> for Signal {
fn from(v: FatalFetchableSignal) -> Self {
match v {
$(FatalFetchableSignal::$fatal_entry => Signal::$fatal_entry),*
}
}
}

impl From<NonFatalFetchableSignal> for FetchableSignal {
fn from(v: NonFatalFetchableSignal) -> Self {
match v {
$(NonFatalFetchableSignal::$entry => FetchableSignal::$entry),*,
NonFatalFetchableSignal::$unknown_entry => FetchableSignal::$unknown_entry,
}
}
}

impl From<NonFatalFetchableSignal> for Signal {
fn from(v: NonFatalFetchableSignal) -> Self {
match v {
$(NonFatalFetchableSignal::$entry => Signal::$entry),*,
NonFatalFetchableSignal::$unknown_entry => Signal::$unknown_entry,
}
}
}

impl From<FetchableSignal> for Signal {
fn from(v: FetchableSignal) -> Self {
match v {
$(FetchableSignal::$entry => Signal::$entry),*,
$(FetchableSignal::$fatal_entry => Signal::$fatal_entry),*,
FetchableSignal::$unknown_entry => Signal::$unknown_entry,
}
}
Expand All @@ -125,33 +182,34 @@ macro_rules! define_signals {

define_signals! {
fetchable:
Abort = posix::SIGABRT,
Alarm = posix::SIGALRM,
Bus = posix::SIGBUS,
Child = posix::SIGCHLD,
//TODO: https://github.com/eclipse-iceoryx/iceoryx2/issues/81
// comment in Continue again when the bindgen bug is fixed
//Continue = posix::SIGCONT,
FloatingPointError = posix::SIGFPE,
Hangup = posix::SIGHUP,
IllegalInstruction = posix::SIGILL,
Abort = posix::SIGABRT,
Interrupt = posix::SIGINT,
BrokenPipe = posix::SIGPIPE,
TerminalQuit = posix::SIGQUIT,
SegmentationFault = posix::SIGSEGV,
Terminate = posix::SIGTERM,
TerminalStop = posix::SIGTSTP,
BackgroundProcessReadAttempt = posix::SIGTTIN,
BackgroundProcessWriteAttempt = posix::SIGTTOU,
UserDefined1 = posix::SIGUSR1,
PollableEvent = posix::SIGPOLL,
ProfilingTimerExpired = posix::SIGPROF,
BadSystemCall = posix::SIGSYS,
TraceTrap = posix::SIGTRAP,
UrgentDataAvailableAtSocket = posix::SIGURG,
VirtualTimerExpired = posix::SIGVTALRM,
VirtualTimerExpired = posix::SIGVTALRM
fatal_fetchable:
Alarm = posix::SIGALRM,
BadSystemCall = posix::SIGSYS,
BrokenPipe = posix::SIGPIPE,
Bus = posix::SIGBUS,
Child = posix::SIGCHLD,
CpuTimeLimitExceeded = posix::SIGXCPU,
FileSizeLimitExceeded = posix::SIGXFSZ
FileSizeLimitExceeded = posix::SIGXFSZ,
FloatingPointError = posix::SIGFPE,
Hangup = posix::SIGHUP,
IllegalInstruction = posix::SIGILL,
SegmentationFault = posix::SIGSEGV,
TraceTrap = posix::SIGTRAP
unknown_translates_to:
UserDefined2 = posix::SIGUSR2
unfetchable:
Expand Down Expand Up @@ -322,8 +380,8 @@ impl SignalHandler {
/// Calls a provided callable and fetches possible signals which where raised indirectly by
/// the call. This is helpful for instance when a low level C call can fail by emitting a
/// signal. On example is `memset` when it writes on a preallocated chunk but the actual
/// memory of the operating system does not suffice.
pub fn call_and_fetch<F: FnOnce()>(call: F) -> Option<FetchableSignal> {
/// memory of the operating system does not suffice then it emits `SIGABRT`
pub fn call_and_fetch<F: FnOnce()>(call: F) -> Option<NonFatalFetchableSignal> {
{
let _sighandle = Self::instance();
LAST_SIGNAL.store(posix::MAX_SIGNAL_VALUE, Ordering::Relaxed);
Expand All @@ -339,35 +397,38 @@ impl SignalHandler {

/// Returns the last signal which was raised. After the call the last signal is reset and would
/// return [`None`] on the next call when no new signal was raised again.
pub fn last_signal() -> Option<FetchableSignal> {
pub fn last_signal() -> Option<NonFatalFetchableSignal> {
Self::instance();
match LAST_SIGNAL.swap(posix::MAX_SIGNAL_VALUE, Ordering::Relaxed) {
posix::MAX_SIGNAL_VALUE => None,
v => Some((v as i32).into()),
}
}

/// Returns true if ([`FetchableSignal::Interrupt`] or [`FetchableSignal::Terminate`]) was emitted
/// Returns true if ([`NonFatalFetchableSignal::Interrupt`] or
/// [`NonFatalFetchableSignal::Terminate`]) was emitted
/// for instance by pressing CTRL+c, otherwise false
pub fn termination_requested() -> bool {
let last_signal = Self::last_signal();
last_signal == Some(FetchableSignal::Interrupt)
|| last_signal == Some(FetchableSignal::Terminate)
last_signal == Some(NonFatalFetchableSignal::Interrupt)
|| last_signal == Some(NonFatalFetchableSignal::Terminate)
}

/// Blocks until the provided signal was raised or an error occurred.
/// ```no_run
/// use iceoryx2_bb_posix::signal::*;
///
/// SignalHandler::wait_for_signal(FetchableSignal::Terminate);
/// SignalHandler::wait_for_signal(NonFatalFetchableSignal::Terminate);
/// ```
pub fn wait_for_signal(signal: FetchableSignal) -> Result<(), SignalWaitError> {
pub fn wait_for_signal(signal: NonFatalFetchableSignal) -> Result<(), SignalWaitError> {
let s = vec![signal];
Self::wait_for_multiple_signals(&s)
}

/// Blocks until the provided vector signals was raised or an error occurred.
pub fn wait_for_multiple_signals(signals: &[FetchableSignal]) -> Result<(), SignalWaitError> {
pub fn wait_for_multiple_signals(
signals: &[NonFatalFetchableSignal],
) -> Result<(), SignalWaitError> {
Self::instance();

let origin = "Signal::wait_for_multiple_signals";
Expand Down Expand Up @@ -401,7 +462,7 @@ impl SignalHandler {
/// }
/// ```
pub fn timed_wait_for_signal(
signal: FetchableSignal,
signal: NonFatalFetchableSignal,
timeout: Duration,
) -> Result<bool, SignalWaitError> {
let signals = vec![signal];
Expand All @@ -411,9 +472,9 @@ impl SignalHandler {
/// Blocks until one of the provided signals was raised or the timeout was reached. If one of the
/// signals raised it returns its value, otherwise [`None`].
pub fn timed_wait_for_multiple_signals(
signals: &Vec<FetchableSignal>,
signals: &Vec<NonFatalFetchableSignal>,
timeout: Duration,
) -> Result<Option<FetchableSignal>, SignalWaitError> {
) -> Result<Option<NonFatalFetchableSignal>, SignalWaitError> {
Self::instance();

let origin = "Signal::timed_wait_for_multiple_signals";
Expand Down Expand Up @@ -474,8 +535,8 @@ impl SignalHandler {
do_repeat_eintr_call: false,
};

for signal in all::<FetchableSignal>().collect::<Vec<_>>() {
sighandle.register_raw_signal(signal, capture_signal as posix::sighandler_t);
for signal in all::<NonFatalFetchableSignal>().collect::<Vec<_>>() {
sighandle.register_raw_signal(signal.into(), capture_signal as posix::sighandler_t);
}

sighandle
Expand Down
37 changes: 21 additions & 16 deletions iceoryx2-bb/posix/tests/signal_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ impl TestFixture {
COUNTER.fetch_add(1, Ordering::SeqCst);
}

pub fn verify(&self, signal: FetchableSignal, counter_value: usize) {
pub fn verify(&self, signal: NonFatalFetchableSignal, counter_value: usize) {
assert_that!(
|| { COUNTER.load(Ordering::SeqCst) },
block_until counter_value
Expand All @@ -70,7 +70,7 @@ fn signal_register_single_handler_works() {
SignalHandler::register(FetchableSignal::UserDefined1, &TestFixture::signal_callback);

Process::from_self().send_signal(Signal::UserDefined1).ok();
test.verify(FetchableSignal::UserDefined1, 1)
test.verify(NonFatalFetchableSignal::UserDefined1, 1)
}

#[test]
Expand All @@ -85,10 +85,10 @@ fn signal_register_multiple_handler_works() {
SignalHandler::register(FetchableSignal::UserDefined2, &TestFixture::signal_callback);

Process::from_self().send_signal(Signal::UserDefined1).ok();
test.verify(FetchableSignal::UserDefined1, 1);
test.verify(NonFatalFetchableSignal::UserDefined1, 1);

Process::from_self().send_signal(Signal::UserDefined2).ok();
test.verify(FetchableSignal::UserDefined2, 2);
test.verify(NonFatalFetchableSignal::UserDefined2, 2);
}

#[test]
Expand All @@ -100,10 +100,10 @@ fn signal_register_handler_with_multiple_signals_works() {
let _guard1 = SignalHandler::register_multiple_signals(&s, &TestFixture::signal_callback);

Process::from_self().send_signal(Signal::UserDefined1).ok();
test.verify(FetchableSignal::UserDefined1, 1);
test.verify(NonFatalFetchableSignal::UserDefined1, 1);

Process::from_self().send_signal(Signal::UserDefined2).ok();
test.verify(FetchableSignal::UserDefined2, 2);
test.verify(NonFatalFetchableSignal::UserDefined2, 2);
}

#[test]
Expand All @@ -122,7 +122,7 @@ fn signal_guard_unregisters_on_drop() {
});

Process::from_self().send_signal(Signal::UserDefined1).ok();
test.verify(FetchableSignal::UserDefined1, 10);
test.verify(NonFatalFetchableSignal::UserDefined1, 10);
}

#[test]
Expand All @@ -144,13 +144,12 @@ fn signal_call_and_fetch_works() {
test_requires!(POSIX_SUPPORT_ADVANCED_SIGNAL_HANDLING);

let _test = TestFixture::new();

let result = SignalHandler::call_and_fetch(|| {
Process::from_self().send_signal(Signal::Interrupt).ok();
nanosleep(TIMEOUT).ok();
});

assert_that!(result, eq Some(FetchableSignal::Interrupt));
assert_that!(result, eq Some(NonFatalFetchableSignal::Interrupt));
}

#[test]
Expand All @@ -167,8 +166,8 @@ fn signal_call_and_fetch_with_registered_handler_works() {
nanosleep(TIMEOUT).ok();
});

assert_that!(result, eq Some(FetchableSignal::UserDefined1));
test.verify(FetchableSignal::UserDefined1, 1);
assert_that!(result, eq Some(NonFatalFetchableSignal::UserDefined1));
test.verify(NonFatalFetchableSignal::UserDefined1, 1);
}

#[test]
Expand All @@ -177,7 +176,10 @@ fn signal_wait_for_signal_blocks() {

let _test = TestFixture::new();

let signals = vec![FetchableSignal::UserDefined2, FetchableSignal::UserDefined1];
let signals = vec![
NonFatalFetchableSignal::UserDefined2,
NonFatalFetchableSignal::UserDefined1,
];
let counter = AtomicI32::new(0);
thread::scope(|s| {
s.spawn(|| {
Expand Down Expand Up @@ -206,7 +208,7 @@ fn signal_wait_twice_for_same_signal_blocks() {
let counter = AtomicI32::new(0);
thread::scope(|s| {
s.spawn(|| {
SignalHandler::wait_for_signal(FetchableSignal::UserDefined2).unwrap();
SignalHandler::wait_for_signal(NonFatalFetchableSignal::UserDefined2).unwrap();
counter.fetch_add(1, Ordering::Relaxed);
});

Expand All @@ -215,7 +217,7 @@ fn signal_wait_twice_for_same_signal_blocks() {
Process::from_self().send_signal(Signal::UserDefined2).ok();

s.spawn(|| {
SignalHandler::wait_for_signal(FetchableSignal::UserDefined2).unwrap();
SignalHandler::wait_for_signal(NonFatalFetchableSignal::UserDefined2).unwrap();
counter.fetch_add(1, Ordering::Relaxed);
});

Expand All @@ -239,7 +241,7 @@ fn signal_timed_wait_blocks_at_least_for_timeout() {
let _test = TestFixture::new();

let start = Time::now_with_clock(ClockType::Monotonic).unwrap();
SignalHandler::timed_wait_for_signal(FetchableSignal::UserDefined2, TIMEOUT).unwrap();
SignalHandler::timed_wait_for_signal(NonFatalFetchableSignal::UserDefined2, TIMEOUT).unwrap();
assert_that!(start.elapsed().unwrap(), time_at_least TIMEOUT);
}

Expand All @@ -249,7 +251,10 @@ fn signal_timed_wait_blocks_until_signal() {

let _test = TestFixture::new();

let signals = vec![FetchableSignal::UserDefined2, FetchableSignal::UserDefined1];
let signals = vec![
NonFatalFetchableSignal::UserDefined2,
NonFatalFetchableSignal::UserDefined1,
];
let counter = AtomicI32::new(0);
thread::scope(|s| {
s.spawn(|| {
Expand Down