sigwait() and signal handler

16,105

Solution 1

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;
}

Solution 2

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().

Solution 3

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!

Solution 4

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;
}
Share:
16,105
Lunar Mushrooms
Author by

Lunar Mushrooms

Updated on August 01, 2022

Comments

  • Lunar Mushrooms
    Lunar Mushrooms almost 2 years

    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.

  • Lunar Mushrooms
    Lunar Mushrooms over 10 years
    my idea was to block SIGABRT in all other threads. So I masked SIG_BLOCK in main() and thus the child threads also wont receive it. I need to unblock it in WaitForAbortThread() for it to receive it. Since no other threads could receive SIGABRT , WaitForAbortThread() is the only thread that could receive it. The problem I am facing is sigwait() blocked for ever (even if one of the other threads calls abort() which sends SIGABRT) !. Meanwhile if I establish a signal handler using sigaction(), the handler is entered.
  • rakib_
    rakib_ over 10 years
    If you want sigwait() to do any other thing, then avoid for loop, but this is the how typically sigwait() is used and this technique is particularity helpful for handling multiple signal otherwise simple signal handling technique should enough.
  • Andrew Henle
    Andrew Henle about 5 years
    fprintf() can not be safely called from within a signal handler. Per footnote 188 of the C standard: "Thus, a signal handler cannot, in general, call standard library functions." POSIX allows for the calling of async-signal-safe functions, but fprintf() isn't one.
  • Rick
    Rick about 5 years
    @AndrewHenle Hmm you are right. Few functions are async-signal-safe as far as I know. Is there any easy substitute that I can use to replace it ? Gald to know.