Search code examples
linuxsignalssigaction

Implementing sleep function using signal handler


In APUE, section 10.19 (sleep, nanosleep, ...), we have an implementation for sleep function. About line 11, there exists the following code

/* block SIGALRM and save current signal mask */
sigemptyset(&newmask);
sigaddset(&newmask, SIGALRM);
sigprocmask(SIG_BLOCK, &newmask, &oldmask);

My question is: what will happen, if we we don't block SIGALRM?

full code is as follows:

sleep(unsigned int seconds)
{
struct sigaction newact, oldact;
sigset_t newmask, oldmask, suspmask;
unsigned int unslept;


/* set our handler, save previous information */
newact.sa_handler = sig_alrm;
sigemptyset(&newact.sa_mask);
newact.sa_flags = 0;
sigaction(SIGALRM, &newact, &oldact);

/* block SIGALRM and save current signal mask */
sigemptyset(&newmask);
sigaddset(&newmask, SIGALRM);
sigprocmask(SIG_BLOCK, &newmask, &oldmask);
alarm(seconds);
suspmask = oldmask;

/* make sure SIGALRM isn’t blocked */
sigdelset(&suspmask, SIGALRM);

/* wait for any signal to be caught */
sigsuspend(&suspmask);

/* some signal has been caught, SIGALRM is now blocked */
unslept = alarm(0);

/* reset previous action */
sigaction(SIGALRM, &oldact, NULL);

/* reset signal mask, which unblocks SIGALRM */
sigprocmask(SIG_SETMASK, &oldmask, NULL);
return(unslept);
}

Solution

  • The goal is for the SIGALRM sighandler to break the blocking sigsuspend call. If the handler isn't blocked, the signal could arrive before thesigsuspend call is entered in which case the code (after returning from the handler) will go on to run sigsuspend where it'll get deadlocked, waiting for the signal that had already arrived.

    It's standard practice to block a signal before atomically unblocking it in sigsuspend, and it's done exactly to prevent these kinds of race conditions.