void unsafe_sighandler_a(int signum) { printf("Received signal %d\n", signum); }
void unsafe_sighandler_b(int signum) { mylist->tail = (struct mylist*) malloc(sizeof(mylist)); ... }
malloc(3) from a signal handler, or any function which depends on it. Failures are rare enough that people think their code is correct, but this can lead to subtle bugs. EINTR: volatile sig_atomic_t signal_received; void sighandler(int) { signal_received++; } ... int retval; do { if (signal_received) { handle_signal(); } } while ((retval = syscall()) == -1 && errno == EINTR) ;
signal_received and syscall() actually entering kernel space. If a signal arrives in this time, it will not force EINTR and the signal delivery could be delayed indefinitely. sigjmp_buf to immediately return from system calls. volatile sig_atomic_t signal_received, jump_is_safe; sigjmp_buf env; void sighandler(int) { signal_received++; if (jump_is_safe) siglongjmp(env, 1); } ... sigsetjmp(signal_received, 1); jump_is_safe = 1; if (!signal_received) { retval = syscall(); } jump_is_safe = 0;
select(2), poll(2), or level-triggered epoll_wait(2)/kevent(2): No problem; the call can be safely repeated. epoll_wait(2)/kevent(2): It is impossible to know now what descriptors have data available, since subsequent calls will no longer return these descriptors. A level-triggered poll mechanism would have to be used in this case, which complicates the code greatly. read(2), readv(2), write(2), or writev(2): It is impossible to know if the IO operation completed successfully. sigsafe.) It's also very hard to implement correctly. Notice several things about the code fragment above:
jump_is_safe is set only after sigsetjmp(2) is called and only for a very narrow window in which no async signal-unsafe functions are called. sigsetjmp(2) and siglongjmp(2) rather than setjmp(2) and longjmp(2); you can specify the signal mask restoration behavior of the sig variants, while it is defined by the platform for the plain functions. signal_received after setting jump_is_safe. These are all important!
Also there's a performance problem - sigsetjmp(..., 1) makes a system call to retrieve the signal mask, so you're slowing down every iteration for correct signal behavior. To avoid that, you'd have to think about the signal mask yourself. Even more opportunities for bugs.
pselect(2). This function is supposed to change the signal mask atomically in the kernel for the duration of operation, supporting error-free operation like this: sigset_t blocked, unblocked; int retval; pthread_sigmask(SIG_SETMASK, &blocked, NULL); ... while ((retval = pselect(..., &unblocked)) == -1 && errno == EINTR) { printf("Signal received.\n"); } ...
select(2) call with pthread_sigmask(2) calls. Thus, pselect(2) may not return EINTR when you expect it to. poll(2) calls and non-blocking IO calls: This method is correct but slow, since it doubles the number of system calls to be made on basic IO operations.
With sigsafe, you can write code like this:
void myhandler(int signum, siginfo_t *info, void *ctx, intptr_t user_data) { sigaddset((sigset_t*) user_data, signum); } int main(void) { ... sigsafe_install_handler(SIGUSR1, &myhandler); sigsafe_install_handler(SIGUSR2, &myhandler); ... } ... void* thread_entry(void *arg) { ... sigsafe_install_tsd((intptr_t) malloc(sizeof sigset_t), &free); ... } void read_some_data(void) { int retval; while ((retval = sigsafe_read(fd, buf, count)) == -EINTR) { sigset_t *received = (sigset_t*) sigsafe_clear_received(); if (sigismember(received, SIGUSR1)) { printf("Received USR1 signal\n"); } if (sigismember(received, SIGUSR2)) { printf("Received USR2 signal\n"); } } ... }
sigwaitinfo(2) may be your easiest correct way. kevent(2)'s built-in signal mechanism or the pipe-write-from-signal-handler methods may work well for you. sigsafe library is non-portable! Everything here relies on alternate system call wrappers implemented in assembly and a signal handler which adjusts the instruction pointer when signals arrive in system calls. This means that there is significant work involved in porting it to a new platform (where platform is a combination of OS and architecture).kevent(2) handling), that pthread_getspecific(2) is async signal-safe. This is not guaranteed by SUSv3.
README file for the full license text.
1.3.5