Search code examples
cmultithreadingsignalsinterruptpause

Why does pause(2) not return in secondary thread?


Consider this example I set up to illustrate this.

#define _POSIX_C_SOURCE 199506L

#include <unistd.h>
#include <stdio.h>
#include <sys/time.h>
#include <errno.h>
#include <signal.h>
#include <pthread.h>

void hand(int sig);
void *thrfn(void *arg);

int main(void)
{
    struct sigaction act;
    struct itimerval timer;
    sigset_t mask;
    pthread_t thr;

    act.sa_handler = hand;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    sigaction(SIGALRM, &act, NULL);
    /* error checking omitted */

    timer.it_interval.tv_sec = 1;
    timer.it_interval.tv_usec = 500000;
    timer.it_value = timer.it_interval;
    /* ultimately I want to build a loop; hence repeating */
    setitimer(ITIMER_REAL, &timer, NULL);

    sigemptyset(&mask);
    pthread_sigmask(SIG_SETMASK, &mask, NULL);
    /* why doesn't this prevent SIGALRM from interrupting main? */
    pthread_create(&thr, NULL, thrfn, NULL);

    puts("Main thread done.");
    getchar();

    return 0;
}

void hand(int sig)
{
    (void)sig;
    write(STDOUT_FILENO, "Handler handled.\n", 17);
}

void *thrfn(void *arg)
{
    sigset_t mask;

    (void)arg;

    sigemptyset(&mask);
    sigaddset(&mask, SIGALRM);
    pthread_sigmask(SIG_SETMASK, &mask, NULL);
    /* why doesn't this make pause() return in this thread? */

    pause();
    puts("Off thread's pause returned.");
    pthread_exit(NULL);
}

Here's the output of this, compiled with gcc:

Main thread done.
Handler handled.

With about one and a half seconds between the messages.

How come my second thread's pause never returns?


Solution

  • I guess the error is in the usage of pthread_sigmask. Using SIG_BLOCK and SIG_UNBLOCK instead of SIG_SETMASK the program behave you were expecting.

    #define _POSIX_C_SOURCE 199506L
    
    #include <unistd.h>
    #include <stdio.h>
    #include <sys/time.h>
    #include <errno.h>
    #include <signal.h>
    #include <pthread.h>
    
    void hand(int sig);
    void *thrfn(void *arg);
    
    int main(void)
    {
        struct sigaction act;
        struct itimerval timer;
        sigset_t mask;
        pthread_t thr;
    
        act.sa_handler = hand;
        sigemptyset(&act.sa_mask);
        act.sa_flags = 0;
        sigaction(SIGALRM, &act, NULL);
        /* error checking omitted */
    
        timer.it_interval.tv_sec = 1;
        timer.it_interval.tv_usec = 500000;
        timer.it_value = timer.it_interval;
        /* ultimately I want to build a loop; hence repeating */
        setitimer(ITIMER_REAL, &timer, NULL);
    
        sigemptyset(&mask);
        sigaddset(&mask, SIGALRM);
        pthread_sigmask(SIG_BLOCK, &mask, NULL);
        /* This prevent SIGALARM interrupring the main */
        pthread_create(&thr, NULL, thrfn, NULL);
    
        puts("Main thread done.");
        getchar();
    
        return 0;
    }
    
    void hand(int sig)
    {
        (void)sig;
        write(STDOUT_FILENO, "Handler handled.\n", 17);
    }
    
    void *thrfn(void *arg)
    {
        sigset_t mask;
    
        (void)arg;
    
        sigemptyset(&mask);
        sigaddset(&mask, SIGALRM);
        pthread_sigmask(SIG_UNBLOCK, &mask, NULL);
        /* This make the pause() return on SIGALARM */
        pause();
        puts("Off thread's pause returned.");
        pthread_exit(NULL);
    }
    

    This program gives the following output:

    $ ./test 
    Main thread done.
    Handler handled.
    Off thread's pause returned.
    

    It's likely you'll find your answers here and here.

    In particular with:

    sigemptyset(&mask);
    pthread_sigmask(SIG_SETMASK, &mask, NULL);
    

    you are unbloking all signals in the main thread. While, with:

    sigemptyset(&mask);
    sigaddset(&mask, SIGALRM);
    pthread_sigmask(SIG_SETMASK, &mask, NULL);
    

    you are blocking only SIGALARM on the child thread. That is probably the contrary of what you would like to do. Keep in mind that using SIG_SETMASK you specify the set of blocked signals.