There is a timer which sends out signal SIGALARM every 1 sec. A signal handler which sleeps
2 sec is registered. What happens? Specifically, I have following code, in which the process runs multiple threads. It’s quite interesting that with this long signal handler, it looks other threads are blocked from execution… Can anyone explain why this is the case?
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h> //rand
#include <sys/wait.h>
#include <time.h>
#include <sys/time.h>
#include <pthread.h>
#define NUM_THREADS 2
int init_timer(int real_time, int msec) {
struct itimerval timeslice;
timeslice.it_interval.tv_sec = msec / 1000;
timeslice.it_interval.tv_usec = (msec % 1000) * 1000;
timeslice.it_value.tv_sec = msec / 1000;
timeslice.it_value.tv_usec = (msec % 1000) * 1000;
setitimer(real_time ? ITIMER_REAL : ITIMER_VIRTUAL, ×lice, NULL);
return 0;
}
void install_handler(int signo, void(*handler)(int)) {
sigset_t set;
struct sigaction act;
/* Setup the handler */
act.sa_handler = handler;
act.sa_flags = SA_RESTART;
sigaction(signo, &act, 0);
/* Unblock the signal */
sigemptyset(&set);
sigaddset(&set, signo);
sigprocmask(SIG_UNBLOCK, &set, NULL);
return;
}
void timerTest(int signo)
{
printf("000\n");
sleep(1);
printf("111\n");
}
void * threadTest(void * threadId)
{
while(true)
{
printf("222\n");
}
}
int main(int argc, char *argv[]) {
int real_time = 1;
int tick_msec = 10;
init_timer(real_time, tick_msec);
install_handler(real_time ? SIGALRM : SIGVTALRM, &timerTest);
pthread_t threads[NUM_THREADS];
int rc;
long t;
for (t = 0; t < NUM_THREADS; t++) {
rc = pthread_create(&threads[t], NULL, threadTest, (void *) t);
if (rc) {
exit(-1);
}
}
void * status;
for (t = 0; t < NUM_THREADS; t++) {
rc = pthread_join(threads[t], &status);
if (rc) {
exit(-1);
}
}
pthread_exit(NULL);
}
printout:
222
222
222
222
...
222
000
111
000
111
...
there will be no 222 after the first 111 occurs? why so?
The signal is delivered to a particular thread, so the signal handler runs in a particular thread (the thread the signal was delivered to). If the signal is delivered to a thread writing out
222\n, then that thread must stop writing out222\nand run the signal handler. Your example signal handler takes a full second to run, so that’s a full second during which that thread may not write out222\n.Additionally, since you are using
printfto write out all of these bytes, there is some locking being done in libc. Sinceprintfis not an “async signal safe” function, it’s actually undefined what happens if you use it in a signal handler. One possible explanation for the behavior you observe is this. If the signal is delivered to a thread while that thread holds the stdout lock, then no other thread will be able to write to stdout until the handler returns and the lock can be released by the “normal” code running in that thread. The signal handler can still write to stdout in this case, though, because the lock is an rlock which can be acquired repeatedly any particular thread. This may vary from depending on the specific platform, C library, thread library, or phase of the moon, though. Your example is easily converted to use write(2) though, which demonstrates more or less the same problem behavior, has more or less the same fix, and doesn’t rely on undefined behavior.If you
SIG_BLOCKthe timer signal in the222\nthreads, then the signal handler will always run in the main thread and you will continue to get222\noutput while the signal handler sleeps.Seth also makes a great point about only using the safe functions in signal handlers. Using any others means your program’s behavior is undefined.