posix threads block signal and unblock

2020-07-10 08:29发布

问题:

Is there a way to block certain signals and unblock other signals in the same set? I just dont seem to get my head around it!

An example

sigset_t set;
sigemptyset(&set);

sigaddset(&set, SIGUSR1);
// Block signal SIGUSR1 in this thread
pthread_sigmask(SIG_BLOCK, &set, NULL);
sigaddset(&set, SIGALRM);
// Listen to signal SIGUSR2
pthread_sigmask(SIG_UNBLOCK, &set, NULL);


pthread_t printer_thread1, printer_thread2;
pthread_create(&printer_thread1, NULL, print, (void *)&f1);
pthread_create(&printer_thread2, NULL, print, (void *)&f2);

bool tl = true;
while(1)
{
    if(tl)
    {
        // thread1 does something
        kill(pid, SIGUSR1);
        // main thread waits for SIGALRM
        sigwait(&set, &sig);
        tl = !tl;
    }
    else
    {
        // thread2 does something
        kill(pid, SIGUSR2);
        // main thread waits for SIGALRM
        sigwait(&set, &sig);
        tl = !tl;
    }
}

I am not allowed to use Mutexs, semaphores etc only signals.

Can someone help? :)

回答1:

Is there a way to block certain signals and unblock other signals in the same set?

With pthread_sigmask, you can choose to either:

  • add a set of signals to the set of blocked signals, using the constant SIG_BLOCK
  • remove a set of signals to the set of blocked signals, using the constant SIG_UNBLOCK
  • define the set of signals to be blocked, using the constant SIG_SET

In other words, there's a current set of blocked signals for the thread, and you can modify it as specified above, one operation at a time.

The important point is that newly created threads inherit the signal mask of the creating thread, so you can set the mask of the new thread right before creating it, or in the function that the new thread will run, at your convenience.

Regarding your example, I suppose that you are trying to have printer_thread1 block SIGUSR2 and SIGALRM, and have printer_thread2 block SIGUSR1 and SIGALRM, and have the main thread block SIGUSR1 and SIGUSR2, so that each thread can send a signal that will be caught by a single thread (without the code of print, f1 and f2, it's impossible to know for sure what is your intent in your example).

You should be able to achieve that by the following code:

sigset_t set;
pthread_t printer_thread1, printer_thread2;


// Block signal SIGUSR1 & SIGALRM in printer_thread1
sigemptyset(&set);
sigaddset(&set, SIGUSR1);
sigaddset(&set, SIGALRM);
pthread_sigmask(SIG_SET, &set, NULL);
pthread_create(&printer_thread1, NULL, print, (void *)&f1);

// Block signal SIGUSR2 & SIGALRM in printer_thread2
sigaddset(&set, SIGUSR2);
sigaddset(&set, SIGALRM);
pthread_sigmask(SIG_SET, &set, NULL);
pthread_create(&printer_thread2, NULL, print, (void *)&f2);

// Block signal SIGUSR1 & SIGUSR2 in the main thread
sigemptyset(&set);
sigaddset(&set, SIGUSR1);
sigaddset(&set, SIGUSR2);
// Listen to signal SIGALRM
pthread_sigmask(SIG_SET, &set, NULL);


bool tl = true;
while(1)
{
    if(tl)
    {
        // thread1 does something
        kill(pid, SIGUSR1);
        // main thread waits for SIGALRM
        sigwait(&set, &sig);
        tl = !tl;
    }
    else
    {
        // thread2 does something
        kill(pid, SIGUSR2);
        // main thread waits for SIGALRM
        sigwait(&set, &sig);
        tl = !tl;
    }
}

see these man pages :

  • pthread_sigmask
  • sigprocmask

for further details.



回答2:

I think what you want to do here is

// Block signal SIGUSR1 in this thread
sigemptyset(&set);
sigaddset(&set, SIGUSR1);
pthread_sigmask(SIG_BLOCK, &set, NULL);

// Listen to signal SIGALRM
sigemptyset(&set);
sigaddset(&set, SIGALRM);
pthread_sigmask(SIG_UNBLOCK, &set, NULL);

The set is only used to tell it what to block or unblock. Once passed to the command, you are free to reset it and build up another signal mask. If you skip the sigemptyset, the set will still contain SIGUSR1, which will subsequently be unblocked again. Well, I think that is how it works, at least - it has been a long time since I used signals.