Hello everyone!
Some operating systems implement a concept of signals, that allow an application to be notified of various system events and intervene the control flow by invoking a signal handling. The standard way is to install a signal handler, however, this is extremely unsafe (because in this case, signal delivery means interrupting a thread and redirecting the control flow), so I think it should be limited to special cases, such as:
- Handling emergency signals (
SIGBUS
,SIGSEGV
,SIGILL
,SIGFPE
,SIGABRT
) -- they can only be caught with a signal handler (if not manually invoked) - Invoking emergency action (that has to be done right now and cannot wait to return to the event loop)
- Forcing a time-out on system calls -- a signal handler shouldn't do anything in that case (however, please note that some libraries can restart the system call if they see it failed with
EINTR
).
For other cases, I recommend synchronous signal handling -- that is, blocking them and handling them and specific points (by unblocking-and-reblocking, using sigtimedwait()
or OS-specific facilities such as signalfd()
in Linux). It can happen inside the event loop of an application. However, if an application creates child processes, this can be a problem, because signal mask is inherited to child processes and is preserved across execve()
-- strictly speaking, the initial signal mask is undefined for the new process after execution, and if it wants to use ordinary signal handlers, it should explicitly unblock signals. However, many apps don't do that and simply assume all signals are unblocked from the beginning. To avoid breaking signal handling in these apps, the process can:
- Use the
pre_exec()
method fromstd::os::unix::process::CommandExt
trait (the solution I prefer) - Install an ordinary signal handler and use the self-pipe trick -- open a non-blocking pipe, then, after the delivery of a signal, drop the contents of
siginfo_t
structure into the write end in the handler - Use the global variable. In C,
volatile sig_atomic_t
variables are used (as far as I know,sig_atomic_t
is a typedef for a type that can be re-assigned in one instruction). I'd like to ask, are types instd::sync::atomic
module considered safe to change in signal handlers?