process synchronization with signals deadlock case - c

I need to synchronize parent and child process to work in order in endless loop but I am getting deadlock after a while.
I am not allowed to use sleep, wait and I must implement the synchronization with signals. After a search I found a similar question which is used pause method, in the answers it is recommended to use sigsuspend rather than pause. So I am not sure if there is problem with my implementation or I need to use another way to avoid deadlock
Heres my code
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <time.h>
void catcher( int sig ) {
printf( "inside catcher() function\n" );
}
void timestamp( char *str ) {
time_t t;
time( &t );
printf( "%s the time is %s\n", str, ctime(&t) );
}
int main( int argc, char *argv[] ) {
struct sigaction sigact;
sigset_t block_set1,block_set2;
sigfillset( &block_set1 );
sigfillset( &block_set2 );
sigdelset( &block_set1, SIGUSR1 );
sigdelset( &block_set2, SIGUSR2 );
sigemptyset( &sigact.sa_mask );
sigact.sa_flags = 0;
sigact.sa_handler = catcher;
sigaction( SIGUSR1, &sigact, NULL );
sigaction( SIGUSR2, &sigact, NULL );
pid_t child_id;
child_id = fork ();
if (child_id == 0){
while(1){
printf("child send signal\n");
kill (getppid (), SIGUSR1);
printf("child wait signal\n");
sigsuspend( &block_set2 );
printf("child is going to die\n");
}
}else{
while(1){
timestamp( "before sigsuspend()" );
sigsuspend( &block_set1 );
timestamp( "after sigsuspend()" );
kill (child_id, SIGUSR2);
printf("parent is going to die\n");
}
}
return( 0 );
}

This program has an issue, Consider a scenario where child/parent sends a signal to parent/child even before they suspend their execution using sigsuspend(). In this case handler will be executed and later no one will be
their to interrupt sigsupend() which may arise to infinite suspend state.
Quoting man page
int sigsuspend(const sigset_t *mask);
sigsuspend() temporarily replaces the signal mask of the calling
process with the mask given by mask and then suspends the process
until delivery of a signal whose action is to invoke a signal handler
or to terminate a process.
I would suggest to use sigwaitinfo() instead,
int sigwaitinfo(const sigset_t *set, siginfo_t *info);
sigwaitinfo() suspends execution of the calling thread until one of
the signals in set is pending (If one of the signals in set is already
pending for the calling thread, sigwaitinfo() will return
immediately.)
Please find the example program for same.
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <time.h>
void timestamp( char *str ) {
time_t t;
time( &t );
printf( "%s the time is %s\n", str, ctime(&t) );
}
int main( int argc, char *argv[] ) {
sigset_t parent_set, child_set;
sigset_t set;
siginfo_t info;
/*Mask SIGUSR2 and SIGUSR1 signal, you can choose this mask
* according to your requirement*/
sigemptyset(&set);
sigaddset(&set, SIGUSR1);
sigaddset(&set, SIGUSR2);
sigprocmask(SIG_SETMASK, &set, NULL);
sigemptyset(&child_set);
sigaddset(&child_set, SIGUSR2);
sigemptyset(&parent_set);
sigaddset(&parent_set, SIGUSR1);
pid_t child_id;
child_id = fork ();
if (child_id == 0){
while(1){
printf("child send signal\n");
kill (getppid (), SIGUSR1);
printf("child wait signal\n");
/* Child will suspend its execution if parent did not notify it
* using signal(SIGUSR2), if parent has already notified sigwaitinfo()
* will return immediately
**/
sigwaitinfo(&child_set, &info );
printf("child is going to die\n");
}
}else{
while(1){
timestamp( "before sigwaitinfo()" );
/* Parent will suspend its execution if child did not notify it
* using signal(SIGUSR1), if child has already notified sigwaitinfo()
* will return immediately
**/
sigwaitinfo(&parent_set, &info );
timestamp( "after sigwaitinfo()" );
kill (child_id, SIGUSR2);
printf("parent is going to die\n");
}
}
}
I hope this will help you.

The problem, as #monc noted, is that one process signals the other when the other process isn't ready.
So, the signal is delivered (i.e., the sigaction() handler is invoked) before the call to sigsuspend(), and thus the delivery cannot interrupt the sigsuspend(). Something like this happens:
while(1){
printf("child send signal\n");
kill (getppid (), SIGUSR1);
// SIGUSR2 delivered here
printf("child wait signal\n"); // or here
// or here
sigsuspend( &block_set2 ); // wait forever...
...
}
The solution is to block (mask) SIGUSR1 in the parent and SIGUSR2 in the child from the very start. Blocking the signals will forestall delivery, keeping them pending until sigsuspend() atomically swaps out the signal mask to allow delivery (and interruption). To be safe, the signals should be blocked before you call fork(), and the child can inherit the signal mask from the parent:
sigemptyset(&ss);
sigaddset(&ss, SIGUSR1);
sigaddset(&ss, SIGUSR2);
sigprocmask(SIG_BLOCK, &ss, NULL);
... fork();
// child
while (1) {
sigset_t wakemask;
sigemptyset(&wakemask);
...
sigsuspend(&wakemask);
}
// similarly for parent ...
Note that in the above the parent and child are each blocking both signals, including the ones they send to each other. That's not necessary — you could unblock right after the fork() — but also not harmful.
Additionally, you could also accept rather than deliver the signals using the sigwait family of functions. In this case you would not need to set a handler with sigaction, but you would still need to block the signals.

Related

sigsuspend() with SIGINT and SIGCHLD

I am studying this piece of code from the CSAPP book:
#include "csapp.h"
volatile sig_atomic_t pid;
void sigchld_handler(int s)
{
int olderrno = errno;
pid = Waitpid(-1, NULL, 0);
errno = olderrno;
}
void sigint_handler(int s)
{
}
#define N 10
int main(int argc, char **argv)
{
sigset_t mask, prev;
int n = N; /* 10 */
Signal(SIGCHLD, sigchld_handler);
Signal(SIGINT, sigint_handler);
Sigemptyset(&mask);
Sigaddset(&mask, SIGCHLD);
while (n--) {
Sigprocmask(SIG_BLOCK, &mask, &prev); /* Block SIGCHLD */
if (Fork() == 0) /* Child */
exit(0);
/* Wait for SIGCHLD to be received */
pid = 0;
while (!pid)
Sigsuspend(&prev);
/* Optionally unblock SIGCHLD */
Sigprocmask(SIG_SETMASK, &prev, NULL);
/* Do some work after receiving SIGCHLD */
printf(".");
}
printf("\n");
exit(0);
}
While I understand a large part of it such as how sigsuspend uses mask and interacts with SIGCHLD, I don't understand this piece from the book If the parent caught a SIGINT, then the loop test succeeds and the next iteration calls sigsuspend again. If the parent caught a SIGCHLD, then the loop test fails and we exit the loop. At this point, SIGCHLD is blocked, and so we can optionally unblock SIGCHLD.
How does SIGINT interact with sigsuspend()? How will loop test succeed if SIGINT gets caught by parent? I guess the behavior of SIGINT is overwritten by the call to Signal(), which gets overwritten to doing nothing?
Also, how is SIGCHLD still blocked after loop test fails(by sigchld_handler())?
Much thanks in advance!
Other than this, can someone please do a walkthrough of this whole example? I don't have a solid understanding after reading it multiple times.

Using signals in a child process

I want to create a simple program that uses fork and creates a child process which with the use of pause is waiting. I want this child process to start after it gets a specific signal from father process. Code I've written:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main() {
pid_t c = fork();
if (c == 0) {
pause();
printf("signal was given");
}
if (c > 0)
kill(c, SIGINT);
return 0;
}
I think kill gives a specific signal to a process with pid c(child) and I thought that pause just waits for a signal to unpause that process. However in this case running this program has no results. I have also tried adding a signal catching function to the child using signal(SIGINT, handler) and creating a handler function that prints the desired result but it is still not working. Any ideas?
If you send SIGINT, whose default disposition is to kill the process, to a process that neither blocks it nor handles it, the process will die.
If you want the signal to interrupt blocking calls like pause(), it needs to have a handler.
But simply installing a handler introduces race conditions:
if (c == 0 ){
//< if the signal arrives here the child dies
signal(SIGINT, handler);
//< if the signal arrives here then nothing happens except the handler is run
pause(); //< if the handler arrives here then pause gets interrupted
printf("signal was given\n");
exit(0);
}
To eliminate the race conditions, you need to
block the signal in the parent so that the child starts with the signal blocked
install the handler in the child
unblock the signal and pause() in one atomic step
To achieve 3. in one step, you need sigsuspend() instead of pause().
#include <stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<signal.h>
void handler(int Sig){}
int main()
{
sigset_t sigint, oldmask; sigemptyset(&sigint); sigaddset(&sigint, SIGINT);
sigprocmask(SIG_BLOCK, &sigint, &oldmask);
pid_t c=fork();
if(0>c) return perror(0),1;
if (c==0){
signal(SIGINT, handler);
sigdelset(&oldmask,SIGINT); /*in (the unlikely) case the process started with SIGINT blocked*/
sigsuspend(&oldmask);
printf("signal was given\n");
exit(0);
}
kill(c,SIGINT);
wait(0);
return 0;
}
Alternatively, you can use sigwait() and drop the need for a handler altogether:
#include <stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<signal.h>
int main()
{
sigset_t sigint, oldmask; sigemptyset(&sigint); sigaddset(&sigint, SIGINT);
sigprocmask(SIG_BLOCK, &sigint, &oldmask);
pid_t c=fork();
if(0>c) return perror(0),1;
if (c==0){
int sig; sigwait(&sigint,&sig);
printf("signal was given\n");
exit(0);
}
kill(c,SIGINT);
wait(0);
return 0;
}
You have two issues:
The child process is getting a signal before it calls pause().
SIGINT by default would kill a process so printf will never be executed.
Try this:
void handler(int signum)
{
//nothing here
}
int main()
{
pid_t c = fork();
if (c == 0) {
signal(SIGINT, handler);
pause();
printf("signal was given");
}
if (c > 0) {
sleep(1); // <-- give the child process some time to pause()
kill(c, SIGINT);
}
return 0;
}

How to fork the youngest process

Let's say that whenever main program receives SIGINT signal, the youngest (i.e. the lastly forked) process is forked. In other words:
p1 SIGINT
p1->p2 SIGINT
p1->p2->p3
and so on
My problem is that I don't know how to tell the grand-grand-...-children to fork.
My problem is that I don't know how to tell the
grand-grand-...-children to fork.
Parent should install SIGINT handler using SA_SIGINFO flag.
When a grandchild is created it should send signal to parent(p1).
P1 through its signal handler(si_pid field of handler's siginfo_t argument) reads pid of grandchild.
When Parent receives SIGINT then it send signal to grand child.
When grand child receives signal it should fork a new child.
Repeat from 2-5 for newly created child.
One more approach:
Let parent poll for SIGINT signal.
When SIGINT received by parent, create child.
Mask SIGINT in parents context and wait for SIGINT in child context.
repeat step 1 to 3 in childs context.
Example:
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#define CHILD_NEEDED 10
static volatile sig_atomic_t got_sigint;
static volatile sig_atomic_t child_created;
void sigint_handler(int sig, siginfo_t *f, void *main_context) {
ucontext_t *uc = main_context;
// to mask SIGINT while returning from handler.
sigaddset(&uc->uc_sigmask, SIGINT);
++child_created;
got_sigint = 1;
}
void create_child(void)
{
pid_t pid = fork();
sigset_t set;
sigemptyset(&set);
if (-1 == pid) {
perror("Fork failed\n");
exit(1);
} else if (0 == pid) {
// Exit if expected child are created
if (child_created == CHILD_NEEDED) {
printf("New Child\nTotal Child: %d\n", child_created);
exit(0);
}
got_sigint = 0;
// As parent mask is copied to child so clear child mask set.
sigprocmask(SIG_SETMASK, &set, NULL);
printf("New Child\n Please send signal to create child\n");
while (!got_sigint);
create_child();
}
}
int main(int argc, const char *argv[]) {
struct sigaction act = {.sa_sigaction = sigint_handler, .sa_mask = 0, .sa_flags = SA_SIGINFO};
sigset_t mask;
int status;
parent = getpid();
sigaction(SIGINT, &act, NULL);
printf("parent\n Please send signal to create child\n");
while (!got_sigint);
// As we have returned from signal so SIGINT will be mask for parent.
create_child();
// parent will wait here.
wait(&status);
exit(0);
}
All your processes will receive the SIGINT signal, so the main process doesn't need to tell the youngest process to fork; just let the process that hasn't forked yet fork, and all other processes do nothing.

No trace of the child process

I forked a child and I am trying to synchronize them so they print
child 0
parent 0
child 1
parent 1
I have to use sigsuspend though, this is my code for the moment and all I get is parent suspend. There is no trace of the child.
int c=0, receivedP=0, receivedC=0;
sigset_t setParent, setChild;
void handler(int s){
if(s==SIGUSR1){
receivedC=1;
printf("parent --sig1--> child\n");
c++;
}
else{
receivedP=1;
printf("child --sig2--> parent\n");
}
}
void child(){
sigfillset(&setChild);
sigdelset(&setChild,SIGUSR1);
sigdelset(&setChild,SIGINT); //this makes me able to terminate the program at any time
while(1){
if(receivedC==0){
printf("child suspend\n");
sigsuspend(&setChild);
}
receivedC=0;
printf("child %d\n",c);
kill(getppid(),SIGUSR2);
}
}
void parent(pid_t pf){
sigfillset(&setParent);
sigdelset(&setParent,SIGUSR2);
sigdelset(&setParent,SIGINT); //this makes me able to terminate the program at any time
kill(pf,SIGUSR1);
while(1){
if(receivedP==0){
printf("parent suspend\n");
sigsuspend(&setParent);
}
receivedP=0;
printf("parent %d\n",c);
kill(pf,SIGUSR1);
}
}
int main(){
signal(SIGUSR1,handler);
signal(SIGUSR2,handler);
pid_t p;
p= fork();
if(!p)child();
else parent(p);
return 0;
}
Anybody knows what's causing this?
I think you are running foul of one of the classic problems with signals.
while(1){
if(receivedP==0){
printf("parent suspend\n");
sigsuspend(&setParent);
}
receivedP=0;
printf("parent %d\n",c);
kill(pf,SIGUSR1);
}
Imagine what happens if the signal from the child arrives in between the instructions for if(receivedP==0) and sigsuspend(&setParent). The handler will execute, and will set receivedP to one, but the main loop won't check it again; it will go into sigsuspend and never come out.
In order to use sigsuspend safely, you need to have the signals you care about be blocked at all times when the program is not calling sigsuspend. You do that with sigprocmask. It's also necessary to ensure that the signals are blocked during the execution of the handler, which requires you to use sigaction instead of signal (but you should do that anyway, as signal is severely underspecified and system-to-system variations will bite you in the ass).
Once you ensure that the signal can only be delivered during a sigsuspend, you no longer need the receivedP and receivedC variables; you know that the signal happened, or sigsuspend would not have returned. (This would not be true if your program was waiting for more than a single signal in each process, but at that point things get much more complicated; don't worry about it till it comes up.)
In fact, once you ensure that, you don't need to do anything in the signal handler. Your counter variable can be local to parent and child. It's always best to do as little in a signal handler as possible; the letter of the C standard allows you to do almost nothing without risking undefined behavior, and POSIX only opens it up a little bit more. (Exercise for you: change this program to use sigwaitinfo so that it doesn't need handler functions at all.)
This modification of your program works reliably for me. I also corrected a number of other style problems and minor errors: note the loops in parent and child doing things in different orders, the error checking in main, and that I am only blocking SIGUSR1 and SIGUSR2, because there are several other signals that should be allowed to terminate the process (SIGTERM, SIGHUP, SIGQUIT, SIGSEGV, …) and you don't want to have to maintain a list. It is sufficient to block the signals that the program has installed handlers for.
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
static void handler(int unused)
{
}
static void child(sigset_t *ss)
{
unsigned int c = 0;
pid_t parent_pid = getppid();
sigdelset(ss, SIGUSR1);
for (;;) {
sigsuspend(ss);
printf("child %u\n", c++);
kill(parent_pid, SIGUSR2);
}
}
static void parent(sigset_t *ss, pid_t child_pid)
{
unsigned int c = 0;
sigdelset(ss, SIGUSR2);
for (;;) {
printf("parent %u\n", c++);
kill(child_pid, SIGUSR1);
sigsuspend(ss);
}
}
int main(void)
{
// Ensure line-buffered stdout.
if (setvbuf(stdout, 0, _IOLBF, 0)) {
perror("setvbuf");
return 1;
}
// This signal mask is in effect at all times _except_ when sleeping
// in sigsuspend(). Note that _only_ the signals used for IPC are
// blocked. After forking, each process will modify it appropriately
// for its own use of sigsuspend(); this does not affect the kernel-side
// copy made by sigprocmask().
sigset_t ss;
sigemptyset(&ss);
sigaddset(&ss, SIGUSR1);
sigaddset(&ss, SIGUSR2);
if (sigprocmask(SIG_BLOCK, &ss, 0)) {
perror("sigprocmask");
return 1;
}
// Always use sigaction(), not signal(); signal() is underspecified.
// The mask here is the signal mask to use _while the handler is
// executing_; it should also block both IPC signals.
struct sigaction sa;
sa.sa_handler = handler;
sa.sa_mask = ss;
sa.sa_flags = SA_RESTART;
if (sigaction(SIGUSR1, &sa, 0) || sigaction(SIGUSR2, &sa, 0)) {
perror("sigaction");
return 1;
}
pid_t child_pid = fork();
if (child_pid < 0) {
perror("fork");
return 1;
}
if (child_pid == 0)
child(&ss);
else
parent(&ss, child_pid);
// we never get here but the compiler might not know that
return 0;
}
I recommend you read the GNU C Library Manual's section on signal handling all the way through; it contains several other bits of helpful advice on using signals safely.

sigwait() and signal handler

If I setup and signal handler for SIGABRT and meanwhile I have a thread that waits on sigwait() for SIGABRT to come (I have a blocked SIGABRT in other threads by pthread_sigmask).
So which one will be processed first ? Signal handler or sigwait() ?
[I am facing some issues that sigwait() is get blocked for ever. I am debugging it currently]
main()
{
sigset_t signal_set;
sigemptyset(&signal_set);
sigaddset(&signal_set, SIGABRT);
sigprocmask(SIG_BLOCK, &signal_set, NULL);
// Dont deliver SIGABORT while running this thread and it's kids.
pthread_sigmask(SIG_BLOCK, &signal_set, NULL);
pthread_create(&tAbortWaitThread, NULL, WaitForAbortThread, NULL);
..
Create all other threads
...
}
static void* WaitForAbortThread(void* v)
{
sigset_t signal_set;
int stat;
int sig;
sigfillset( &signal_set);
pthread_sigmask( SIG_BLOCK, &signal_set, NULL ); // Dont want any signals
sigemptyset(&signal_set);
sigaddset(&signal_set, SIGABRT); // Add only SIGABRT
// This thread while executing , will handle the SIGABORT signal via signal handler.
pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL);
stat= sigwait( &signal_set, &sig ); // lets wait for signal handled in CatchAbort().
while (stat == -1)
{
stat= sigwait( &signal_set, &sig );
}
TellAllThreadsWeAreGoingDown();
sleep(10);
return null;
}
// Abort signal handler executed via sigaction().
static void CatchAbort(int i, siginfo_t* info, void* v)
{
sleep(20); // Dont return , hold on till the other threads are down.
}
Here at sigwait(), i will come to know that SIGABRT is received. I will tell other threads about it. Then will hold abort signal handler so that process is not terminated.
I wanted to know the interaction of sigwait() and the signal handler.
From sigwait() documentation :
The sigwait() function suspends execution of the calling thread until
one of the signals specified in the signal set becomes pending.
A pending signal means a blocked signal waiting to be delivered to one of the thread/process. Therefore, you need not to unblock the signal like you did with your pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL) call.
This should work :
static void* WaitForAbortThread(void* v){
sigset_t signal_set;
sigemptyset(&signal_set);
sigaddset(&signal_set, SIGABRT);
sigwait( &signal_set, &sig );
TellAllThreadsWeAreGoingDown();
sleep(10);
return null;
}
I got some information from this <link>
It says :
To allow a thread to wait for asynchronously generated signals, the threads library provides the sigwait subroutine. The sigwait subroutine blocks the calling thread until one of the awaited signals is sent to the process or to the thread. There must not be a signal handler installed on the awaited signal using the sigwait subroutine.
I will remove the sigaction() handler and try only sigwait().
From the code snippet you've posted, it seems you got the use of sigwait() wrong. AFAIU, you need WaitForAbortThread like below:
sigemptyset( &signal_set); // change it from sigfillset()
for (;;) {
stat = sigwait(&signal_set, &sig);
if (sig == SIGABRT) {
printf("here's sigbart.. do whatever you want.\n");
pthread_kill(tid, signal); // thread id and signal
}
}
I don't think pthread_sigmask() is really needed. Since you only want to handle SIGABRT, first init signal_set as empty then simply add SIGABRT, then jump into the infinite loop, sigwait will wait for the particular signal that you're looking for, you check the signal if it's SIGABRT, if yes - do whatever you want. NOTE the uses of pthread_kill(), use it to sent any signal to other threads specified via tid and the signal you want to sent, make sure you know the tid of other threads you want to sent signal. Hope this will help!
I know this question is about a year old, but I often use a pattern, which solves exactly this issue using pthreads and signals. It is a little length but takes care of any issues I am aware of.
I recently used in combination with a library wrapped with SWIG and called from within Python. An annoying issue was that my IRQ thread waiting for SIGINT using sigwait never received the SIGINT signal. The same library worked perfectly when called from Matlab, which didn't capture the SIGINT signal.
The solution was to install a signal handler
#define _NTHREADS 8
#include <signal.h>
#include <pthread.h>
#include <unistd.h>
#include <sched.h>
#include <linux/unistd.h>
#include <sys/signal.h>
#include <sys/syscall.h>
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h> // strerror
#define CallErr(fun, arg) { if ((fun arg)<0) \
FailErr(#fun) }
#define CallErrExit(fun, arg, ret) { if ((fun arg)<0) \
FailErrExit(#fun,ret) }
#define FailErrExit(msg,ret) { \
(void)fprintf(stderr, "FAILED: %s(errno=%d strerror=%s)\n", \
msg, errno, strerror(errno)); \
(void)fflush(stderr); \
return ret; }
#define FailErr(msg) { \
(void)fprintf(stderr, "FAILED: %s(errno=%d strerror=%s)\n", \
msg, errno, strerror(errno)); \
(void)fflush(stderr);}
typedef struct thread_arg {
int cpu_id;
int thread_id;
} thread_arg_t;
static jmp_buf jmp_env;
static struct sigaction act;
static struct sigaction oact;
size_t exitnow = 0;
pthread_mutex_t exit_mutex;
pthread_attr_t attr;
pthread_t pids[_NTHREADS];
pid_t tids[_NTHREADS+1];
static volatile int status[_NTHREADS]; // 0: suspended, 1: interrupted, 2: success
sigset_t mask;
static pid_t gettid( void );
static void *thread_function(void *arg);
static void signalHandler(int);
int main() {
cpu_set_t cpuset;
int nproc;
int i;
thread_arg_t thread_args[_NTHREADS];
int id;
CPU_ZERO( &cpuset );
CallErr(sched_getaffinity,
(gettid(), sizeof( cpu_set_t ), &cpuset));
nproc = CPU_COUNT(&cpuset);
for (i=0 ; i < _NTHREADS ; i++) {
thread_args[i].cpu_id = i % nproc;
thread_args[i].thread_id = i;
status[i] = 0;
}
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
pthread_mutex_init(&exit_mutex, NULL);
// We pray for no locks on buffers and setbuf will work, if not we
// need to use filelock() on on FILE* access, tricky
setbuf(stdout, NULL);
setbuf(stderr, NULL);
act.sa_flags = SA_NOCLDSTOP | SA_NOCLDWAIT;
act.sa_handler = signalHandler;
sigemptyset(&act.sa_mask);
sigemptyset(&mask);
sigaddset(&mask, SIGINT);
if (setjmp(jmp_env)) {
if (gettid()==tids[0]) {
// Main Thread
printf("main thread: waiting for clients to terminate\n");
for (i = 0; i < _NTHREADS; i++) {
CallErr(pthread_join, (pids[i], NULL));
if (status[i] == 1)
printf("thread %d: terminated\n",i+1);
}
// On linux this can be done immediate after creation
CallErr(pthread_attr_destroy, (&attr));
CallErr(pthread_mutex_destroy, (&exit_mutex));
return 0;
}
else {
// Should never happen
printf("worker thread received signal");
}
return -1;
}
// Install handler
CallErr(sigaction, (SIGINT, &act, &oact));
// Block SIGINT
CallErr(pthread_sigmask, (SIG_BLOCK, &mask, NULL));
tids[0] = gettid();
srand ( time(NULL) );
for (i = 0; i < _NTHREADS; i++) {
// Inherits main threads signal handler, they are blocking
CallErr(pthread_create,
(&pids[i], &attr, thread_function,
(void *)&thread_args[i]));
}
if (pthread_sigmask(SIG_UNBLOCK, &mask, NULL)) {
fprintf(stderr, "main thread: can't block SIGINT");
}
printf("Infinite loop started - CTRL-C to exit\n");
for (i = 0; i < _NTHREADS; i++) {
CallErr(pthread_join, (pids[i], NULL));
//printf("%d\n",status[i]);
if (status[i] == 2)
printf("thread %d: finished succesfully\n",i+1);
}
// Clean up and exit
CallErr(pthread_attr_destroy, (&attr));
CallErr(pthread_mutex_destroy, (&exit_mutex));
return 0;
}
static void signalHandler(int sig) {
int i;
pthread_t id;
id = pthread_self();
for (i = 0; i < _NTHREADS; i++)
if (pids[i] == id) {
// Exits if worker thread
printf("Worker thread caught signal");
break;
}
if (sig==2) {
sigaction(SIGINT, &oact, &act);
}
pthread_mutex_lock(&exit_mutex);
if (!exitnow)
exitnow = 1;
pthread_mutex_unlock(&exit_mutex);
longjmp(jmp_env, 1);
}
void *thread_function(void *arg) {
cpu_set_t set;
thread_arg_t* threadarg;
int thread_id;
threadarg = (thread_arg_t*) arg;
thread_id = threadarg->thread_id+1;
tids[thread_id] = gettid();
CPU_ZERO( &set );
CPU_SET( threadarg->cpu_id, &set );
CallErrExit(sched_setaffinity, (gettid(), sizeof(cpu_set_t), &set ),
NULL);
int k = 8;
// While loop waiting for exit condition
while (k>0) {
sleep(rand() % 3);
pthread_mutex_lock(&exit_mutex);
if (exitnow) {
status[threadarg->thread_id] = 1;
pthread_mutex_unlock(&exit_mutex);
pthread_exit(NULL);
}
pthread_mutex_unlock(&exit_mutex);
k--;
}
status[threadarg->thread_id] = 2;
pthread_exit(NULL);
}
static pid_t gettid( void ) {
pid_t pid;
CallErr(pid = syscall, (__NR_gettid));
return pid;
}
I run serveral tests and the conbinations and results are:
For all test cases, I register a signal handler by calling sigaction in the main thread.
main thread block target signal, thread A unblock target signal by calling pthread_sigmask, thread A sleep, send target signal.
result: signal handler is executed in thread A.
main thread block target signal, thread A unblock target signal by calling pthread_sigmask, thread A calls sigwait, send target signal.
result: sigwait is executed.
main thread does not block target signal, thread A does not block target signal, thread A calls sigwait, send target signal.
result: main thread is chosen and the registered signal handler is executed in the main thread.
As you can see, conbination 1 and 2 are easy to understand and conclude.
It is:
If a signal is blocked by a thread, then the process-wide signal handler registered by sigaction just can't catch or even know it.
If a signal is not blocked, and it's sent before calling sigwait, the process-wide signal handler wins. And that's why APUE the books require us to block the target signal before calling sigwait. Here I use sleep in thread A to simulate a long "window time".
If a signal is not blocked, and it's sent when sigwait has already been waiting, sigwait wins.
But you should notice that for test case 1 and 2, main thread is designed to block the target signal.
At last for test case 3, when main thread is not blocked the target signal, and sigwait in thread A is also waiting, the signal handler is executed in the main thread.
I believe the behaviour of test case 3 is what APUE talks about:
From APUE §12.8:
If a signal is being caught (the process has established a signal
handler by using sigaction, for example) and a thread is waiting for
the same signal in a call to sigwait, it is left up to the
implementation to decide which way to deliver the signal. The
implementation could either allow sigwait to return or invoke the
signal handler, but not both.
Above all, if you want to accomplish one thread <-> one signal model, you should:
block all signals in the main thread with pthread_sigmask (subsequent thread created in main thread inheris the signal mask)
create threads and call sigwait(target_signal) with target signal.
test code
#define _POSIX_C_SOURCE 200809L
#include <signal.h>
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
FILE* file;
void* threadA(void* argv){
fprintf(file, "%ld\n", pthread_self());
sigset_t m;
sigemptyset(&m);
sigaddset(&m, SIGUSR1);
int signo;
int err;
// sigset_t q;
// sigemptyset(&q);
// pthread_sigmask(SIG_SETMASK, &q, NULL);
// sleep(50);
fprintf(file, "1\n");
err = sigwait(&m, &signo);
if (err != 0){
fprintf(file, "sigwait error\n");
exit(1);
}
switch (signo)
{
case SIGUSR1:
fprintf(file, "SIGUSR1 received\n");
break;
default:
fprintf(file, "?\n");
break;
}
fprintf(file, "2\n");
}
void hello(int signo){
fprintf(file, "%ld\n", pthread_self());
fprintf(file, "hello\n");
}
int main(){
file = fopen("daemon", "wb");
setbuf(file, NULL);
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_handler = hello;
sigaction(SIGUSR1, &sa, NULL);
sigset_t n;
sigemptyset(&n);
sigaddset(&n, SIGUSR1);
// pthread_sigmask(SIG_BLOCK, &n, NULL);
pthread_t pid;
int err;
err = pthread_create(&pid, NULL, threadA, NULL);
if(err != 0){
fprintf(file, "create thread error\n");
exit(1);
}
pause();
fprintf(file, "after pause\n");
fclose(file);
return 0;
}
run with ./a.out & (run in the background), and use kill -SIGUSR1 pid to test. Do not use raise. raise, sleep, pause are thread-wide.

Resources