PThreads: Semi-FAQ Revivison 5
Search
Google
Web
cognitus.net

POSIX Threads: Semi-FAQ Revision 5.2

© 2001-2006 Michael M. Lampkin
email: michael.lampkin<at>ieee.org

8. PTHREADS & SIGNALS

The behavior of signals in a multi-threaded program is quite different from the behavior in a non-threaded program. Here is a list of the basic things you can expect from mixing threads and signals:

 * if a process receives a signal then one and only one indeterminate thread in the process which is not blocking the signal will receive the signal.

 * if a process receives a signal and more than one thread is waiting on that signal, then one and only one indeterminate thread out of that waiting group will receive the signal.

 * if any signal which would normally cause the termination of a process is sent to a thread it will result in the parent process and all threads being terminated.

 * on creation a thread inherits the signal mask of the thread that created it.

Some of the functions used to manipulate and receive signals are also different with the primary ones discussed being:

  • sigemptyset( )
  • sigfillset( )
  • sigaddset( )
  • sigdelset( )
  • pthread_sigmask( )
  • sigwait( )
  • sigwaitinfo( )
  • sigtimedwait( )
  • pthread_kill( )

One last note on this section. It comes nowhere near to providing an in depth discussion of signal handling with threads. For now it simply explains some of the more basic aspects performing synchronous signal handling. With any luck more information will be added as time goes on.

8.1 What is a signal set and how do I create one?

A signal set is nothing more than a variable of type sigset_t as defined in the signal.h header file. In pthreads and POSIX in general it is used to set and retrieve the signals blocked / unblocked by a process or thread and to indicate interest in specific signals to which the sig...wait( ) functions will respond.

Prior to first use a signal set should be initialized using one of two functions. The first function that we will look at is sigemptyset. A call to this function will initialize the given signal set so that all signals are EXCLUDED from the set.

The sigemptyset( ) function has the following prototype:

int sigemptyset
(
   sigset_t * set
);

The second function that we will look at is sigfillset. As you can imagine this does exactly the opposite of sigempty set. That is to say, a call to this function will initialize the given signal set so that all signals are INCLUDED in the set.

int sigfillset
(
   sigset_t * set
);

One thing about sigemptyset( ) and sigfillset( ) is that while both return integer values, neither have any possible error conditions defined so the return is safely ignored. Once you have done the initialization of the signal set, it would normally be time to do some fine grained adjustments and add or remove specific signals from the set.

First we will look at adding a single signal to a set. This operation is facilitated using the sigaddset( ) function shown below:

int sigaddset
(
   sigset_t * set,
   int signo
);

Where signo is the signal to add to the signal set "set".

This can throw an error and return a value of -1 placing EINVAL in errno. This will only occur if the value of signo is invalid or unsupported on the target system.

To delete a single signal from a set the sigdelset( ) function is provided. The prototype is:

int sigdelset
(
   sigset_t * set,
   int signo

);

Where signo is the signal to add to the signal set "set".

This can throw an error just like sigaddset, in particular giving a -1 and EINVAL in the errno variable. As with sigaddset this will only occur if the value of signo is invalid or unsupported on the target system.

A quick code fragment showing the use of some of these functions and creating a signal set that only contained SIGINT ( control-c interrupt ) would be:

#include <signal.h>

...

/* The signal set we will manipulate */

( void ) sigset_t signal_mask;

/* Init the signal set so that it is empty */

( void ) sigemptyset( & signal_mask );

/* Now add SIGINT to the signal set */

( void ) sigaddset( & signal_mask, SIGINT );

8.2 How can I tell if a signal is in a specific signal set?

If you have a signal set and want to determine what signals are ( or are not ) contained in it the function to use is defined in signal.h as follows:

int sigismember
(
   const sigset_t * set,
   int signo
);

Where signo is the signal you want to check for existence in the signal set parameter "set". The function will return 0 if signo DOES NOT exist in the set and a 1 if it DOES exist.

The only possible error is EINVAL which will be indicated by a return value of -1 and passed via errno if the value of signo is invalid or unsupported on the target system.

8.3 How do I get the signal mask of a thread?

The same function is used to both get and set the signal mask of a thread. It is defined in pthread.h as:

int pthread_sigmask
(
   int how,
   const sigset_t * restrict set,
   sigset_t * restrict oset
);

To perform retrieval the how parameter should be SIG_BLOCK ( or actually any of the valid constants since retrieval ops should ignore this value ). Additionally the set parameter should be null and the oset parameter should be a previously created and cleared signal set. On return, the oset parameter will contain the thread's signal mask.

For example:

#include <pthread.h>
#include <signal.h>

...

sigset_t my_set;

sigemptyset( & my_set );

( void ) pthread_sigmask( SIG_BLOCK, NULL, & my_set );

The pthread_sigmask should not return an error during a get operation except in the situation where you put an invalid value in the "how" parameter. Such a case is an obvious error and will cause the function to return EINVAL.

8.4 How do I set the signal mask of a thread?

As mentioned in the section on getting signal masks, the same function is used to both get and set the signal mask of a thread. It is defined in pthread.h as:

int pthread_sigmask
(
   int how,
   const sigset_t * restrict set,
   sigset_t * restrict oset
);

Where the allowed parameter values are:

how: Indicates how the thread’s signal mask will be manipulated and can be one of the following three values.

   SIG_BLOCK: The new signal mask for the thread will consist of the union of the current set and the signal set pointed to by set.

   SIG_SETMASK: The new signal mask for the thread will consist of the signal set pointed to by set.

   SIG_UNBLOCK: The new signal mask will consist of the intersection of the current set and the complement of the signal set pointed to by set.

set: The new signal set to apply to the thread in accordance to the rules specified by the given "how" value. If this value is null then oset must be non-null AND a get operation will be performed.

oset: The signal set where the thread's prior signal mask will be placed. If a copy of the prior mask is not desired the value of this parameter can be null.

The only error condition defined for this function is the use of an unknown value for the "how" parameter. In such a case, the error value of EINVAL will be returned.

For example:

#define _POSIX_C_SOURCE 200112L

#include <pthread.h>
#include <signal.h>
#include <stdio.h>

int main ( int argc, char ** argv )
{
   int sig_num;

   sigset_t new_set;
   sigset_t old_set;

   sigemptyset( & new_set );
   sigemptyset( & old_set );

   ( void ) sigaddset( & new_set, SIGINT );

   /* Set the signal mask of the thread */

   ( void ) pthread_sigmask( SIG_BLOCK, & new_set, & old_set );

   /* Get the modified signal of the thread */

   ( void ) pthread_sigmask( SIG_BLOCK, NULL, & new_set );

   for ( sig_num = 0; sig_num < SIGUSR2; ++ sig_num )
   {
     if ( 0 != sigismember( & old_set, sig_num ) )
     {
       printf( "Old set blocking signal: %i\n", sig_num );
     }
     else
     {
       printf( "Old set not blocking signal: %i\n", sig_num );
     }

     if ( 0 != sigismember( & new_set, sig_num ) )
     {
       printf( "New set blocking signal: %i\n", sig_num );
     }
     else
     {
       printf( "New set not blocking signal: %i\n", sig_num );
     }
   }

   return 0;
}

8.5 Can I use sigprocmask( ) instead of pthread_sigmask( )?

You can definitely use sigprocmask( ) instead of pthread_sigmask( ) if your program is going to run as a single threaded process. If you try to use it in a program that is running as multi-threaded process you WILL get unexpected and undesirable results.

On the other hand pthread_sigmask( ) will work just fine with both single and multi-threaded processes. Since this is the case there really aren't many circumstances not to use pthread_sigmask( ).

8.6 What is the signal mask for a newly created thread?

Any thread that is created inherits the signal mask from it's parent. If a signal mask different from the parent is required then modification of the mask must be performed by the thread after it's creation.

8.7 Using sigwait( ) to receive and process a signal

The sigwait function is the easy way to "catch" signals in a synchronous manner. In simplest terms by synchronous we mean that the thread calling the function will suspended until at least one of the specified signal(s) received.

The function prototype is:
int sigwait
(
   const sigset_t * restrict set,
   int * restrict sig
);

Where the parameters are defined as:

set: A signal set containing the signal(s) the wait function is willing to receive.

sig: The signal, which will be one of those specified in "set", that was received and caused the function to return.

The only possible error is if the set param contains an invalid or unsupported signal in which case the function will return a -1 and EINVAL in errno.

Some comments and common errors concerning sigwait( ) are:

 * If you call sigwait( ) to wait on a particular signal or set of signals then that signal or signals should under most circumstances also be blocked in ALL threads including the thread which called sigwait( ).

 * If you call sigwait( ) to wait on a particular signal or set of signals and that signal or signal is not blocked in one or more other threads it is indeterminate whether the waiting thread will return from sigwait( ) or if one of the threads with the signal(s) unblocked will receive that signal in an asynchronous manner.

 * If you call sigwait( ) to wait on a particular signal or set of signals in more than one thread then ONLY ONE waiting thread will return from sigwait( ).

 * If you call sigwait( ) to wait on a particular signal or set of signals in more than one thread then it is indeterminate WHICH of the waiting threads will actually return from sigwait( ).

 * If you call sigwait( ) you will only be provided with the signal which was received and no other information. See sigwaitinfo( ) and sigtimedwait( ) for information on how to retrieve additional information on the signal.

 * If you call sigwait( ) and none of the signals indicated in set are ever received the calling thread will block forever unless the thread catches another unblocked, caught signal. In such a case the function will return a -1 and EINTR in the errno variable. See sigtimedwait( ) for information on doing a timed wait.

8.8 Using sigwaitinfo( ) to receive and process a signal

In many cases you require more information than what is provided by a simple sigwait( ) call as described previously. In such circumstance you can utilize the sigwaitinfo( ) call and have a siginfo_t structure filled with extended data.

The sigwaitinfo prototype is:

int sigwaitinfo
(
   const sigset_t * restrict set,
   siginfo_t * restrict info
);

All behavior of this function is the same as a sigwait( ) except for the fact that a siginfo_t structure is populated instead of a simple integer indicating the received signal number.

While a full description of all the siginfo_t members is beyond the scope of this document the basic definition taken directly from the POSIX standard is:

  • int si_signo Signal number.
  • int si_errno If non-zero, an errno value associated with this signal, as defined in errno.h
  • int si_code Signal code.
  • pid_t si_pid Sending process ID.
  • uid_t si_uid Real user ID of sending process.
  • void * si_addr Address of faulting instruction.
  • int si_status Exit value or signal.
  • long si_band Band event for SIGPOLL.
  • union sigval si_value Signal value.

8.9 Using sigtimedwait( ) to receive and process a signal

Since both sigwait( ) and sigwaitinfo( ) both suspend the calling thread until one of the specified signals is received or they are interrupted by a unblocked and caught signal they can be hard to exit from at times. To facilitate an easy and timed exit while waiting for a signal the POSIX standard adds the function sigtimedwait( ) to it's definitions.

The prototype of sigtimedwait( ) is:

int sigtimedwait
(
   const sigset_t * restrict set,
   siginfo_t * restrict info,
   const struct timespec * restrict timeout
);

Where the set and info parameters are identical to those described under the sigwaitinfo( ) section and:

timeout: A structure of type timespec which specifies the desired maximum period to wait for a signal in the specified set to be received.

The timespec structure is defined in time.h ( which should be included ) as requiring the following fields:

time_t tv_sec Seconds

long tv_nsec Nanoseconds

A call to this function acts the same as a sigwaitinfo( ) call but has an extra field which specifies the desired maximum amount of time the thread should be suspended while waiting to receive one of the desired signals. If this timer expires prior to the function receiving a signal it will return a -1 and EAGAIN in the errno.

The function can also return a -1 and EINTR errno if the call is interrupted by an unblocked and caught signal. This behavior is identical to both the sigwait( ) and sigwaitinfo( ) functions.

Last but not least, if the tv_nsec value of the timeout structure is less than zero or greater than 999 million the function will return immediately with a -1 and an EINVAL errno.

8.10 How can I send a signal to a specific thread?

The function pthread_kill( ) in signal.h allows you to send a signal to a specific thread in your process.

The function is defined as:

int pthread_kill
(
   pthread_t thread,
   int sig
);

Where the parameters are:

thread: The thread which will receive the signal.

sig: The signal to send to the target thread.

The function will return a zero on success, a ESRCH if the given thread is not valid and a EINVAL is the signal specified is invalid or unsupported.

If you are familiar with the kill( ) function then you are probably also familiar with using it to send a signal value of 0 to a process so that you can detect if the process is still alive. In the same way, pthread_kill( ) can be used with a signal value of 0 to detect if the specified thread is still alive.

8.11 What is the "recommended" setup for signal handling with threads?

This is only a recommendation and my personal recommendation at that. It is meant to simplify signal handling by forcing the entire program to operate ONLY in a synchronous manner and avoid many of the pitfalls associated with asynchronous signal handling with pthreads.

* In your parent thread set the signal mask to block ALL signals including those you want to monitor.

* In your child threads leave the default signal mask which would be the same as the parent with ALL signals blocked.

* Create a single special child thread who has the sole task of calling sigwait( ) or one of the advanced wait functions and receives ALL signals.

* Create an "Observer / Observable" or similar type pattern relationship between the regular child threads and the special child processing signals.

Again, this ONLY applies to a program using synchronous signal handling. If you are doing real time asynchronous signal handling then this setup would not be appropriate or workable.

8.12 Why is the wrong thread receiving a signal?

Some other possibilities and things to check:

* the missing signal(s) is unblocked in more than one thread.

* the missing signal(s) is being waited on by more than one thread using sigwait( ) calls.

* the code is has a combination of the first two items e.g. there exists one thread with the signal unblocked and another thread waiting on it with sigwait.

* the code is mixing non-threaded and pthread signal calls.

There is the possibility that you are using an older system which incorrectly implements the pthreads signaling components. Just one example of this is the old version of pthreads on Linux that had some very quirky behavior when dealing with signals and thread. In particular it would seem to chose a single thread to send ALL signals to for the lifetime of the parent process with evidently no regard to signal masks.

8.13 A short example of pthreads and catching signals synchronously

#define _POSIX_C_SOURCE 200112L
#define NUM_LOOPS 5

#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

int main ( int argc, char ** argv )
{
   int count;
   int result;

   sigset_t mask;
   siginfo_t info;
   struct timespec timeout;

   /* Create a mask holding only SIGINT - ^C Interrupt */

   sigemptyset( & mask );
   sigaddset( & mask, SIGINT );

   /* Set the mask for our main thread to include SIGINT */

   pthread_sigmask( SIG_BLOCK, & mask, NULL );

   /* Max wait for ^C set to 5 seconds */

   timeout.tv_sec = 5;
   timeout.tv_nsec = 0;

   /*
      Receive max combination of NUM_LOOP signals
      or timeouts from sigtimedwait
   */

   for ( count = 0; count < NUM_LOOPS; ++ count )
   {
     result = sigtimedwait( & mask, & info, & timeout );

     if ( –1 == result && EAGAIN == errno )
     {
       printf( "Timeout occurred in sigtimedwait( ) call.\n" );
     }
     else
     {
       switch ( info.si_signo )
       {
         case SIGINT:
           printf( "Caught SIGINT in sigtimedwait( )\n" );
         break;

         /* Should never really get to default */

         default:
           printf( "Caught unexpected signal %i\n", info.si_signo );
         break;
       }
     }
   }

   return 0;
}

©2003-2006 Michael M. Lampkin

All rights reserved.

Use of this website signifies your agreement to the Terms of Use.