13.5 Signal Handling and Threads

Team-FLY

All threads in a process share the process signal handlers, but each thread has its own signal mask. The interaction of threads with signals involves several complications because threads can operate asynchronously with signals. Table 13.2 summarizes the three types of signals and their corresponding methods of delivery.

Table 13.2. Signal delivery in threads.

type

delivery action

asynchronous

delivered to some thread that has it unblocked

synchronous

delivered to the thread that caused it

directed

delivered to the identified thread ( pthread_kill )

Signals such as SIGFPE (floating-point exception) are synchronous to the thread that caused them (i.e., they are always generated at the same point in the thread's execution). Other signals are asynchronous because they are not generated at a predictable time nor are they associated with a particular thread. If several threads have an asynchronous signal unblocked, the thread runtime system selects one of them to handle the signal. Signals can also be directed to a particular thread with pthread_kill .

13.5.1 Directing a signal to a particular thread

The pthread_kill function requests that signal number sig be generated and delivered to the thread specified by thread .

  SYNOPSIS  #include <signal.h>   #include <pthread.h>   int pthread_kill(pthread_t thread, int sig);  POSIX:THR  

If successful, pthread_kill returns 0. If unsuccessful , pthread_kill returns a nonzero error code. In the latter case, no signal is sent. The following table lists the mandatory errors for pthread_kill .

error

cause

EINVAL

sig is an invalid or unsupported signal number

ESRCH

no thread corresponds to specified ID

Example 13.23

The following code segment causes a thread to kill itself and the entire process.

 if (pthread_kill(pthread_self(), SIGKILL))    fprintf(stderr, "Failed to commit suicide\n"); 

Example 13.23 illustrates an important point regarding pthread_kill . Although pthread_kill delivers the signal to a particular thread, the action of handling it may affect the entire process. A common confusion is to assume that pthread_kill always causes process termination, but this is not the case. The pthread_kill just causes a signal to be generated for the thread. Example 13.23 causes process termination because the SIGKILL signal cannot be caught, blocked or ignored. The same result occurs for any signal whose default action is to terminate the process unless the process ignores, blocks or catches the signal. Table 8.1 lists the POSIX signals with their symbolic names and default actions.

13.5.2 Masking signals for threads

While signal handlers are process-wide, each thread has its own signal mask. A thread can examine or set its signal mask with the pthread_sigmask function, which is a generalization of sigprocmask to threaded programs. The sigprocmask function should not be used when the process has multiple threads, but it can be called by the main thread before additional threads are created. Recall that the signal mask specifies which signals are to be blocked (not delivered). The how and set parameters specify the way the signal mask is to be modified, as discussed below. If the oset parameter is not NULL , the pthread_sigmask function sets *oset to the thread's previous signal mask.

  SYNOPSIS  #include <pthread.h>   #include <signal.h>   int pthread_sigmask(int how, const sigset_t *restrict set,                       sigset_t *restrict oset);  POSIX:THR  

If successful, pthread_sigmask returns 0. If unsuccessful, pthread_sigmask returns a nonzero error code. The pthread_sigmask function returns EINVAL if how is not valid.

A how value of SIG_SETMASK causes the thread's signal mask to be replaced by set . That is, the thread now blocks all signals in set but does not block any others. A how value of SIG_BLOCK causes the additional signals in set to be blocked by the thread (added to the thread's current signal mask). A how value of SIG_UNBLOCK causes any of the signals in set that are currently being blocked to be removed from the thread's current signal mask (no longer be blocked).

13.5.3 Dedicating threads for signal handling

Signal handlers are process-wide and are installed with calls to sigaction as in single-threaded processes. The distinction between process-wide signal handlers and thread-specific signal masks is important in threaded programs.

Recall from Chapter 8 that when a signal is caught, the signal that caused the event is automatically blocked on entry to the signal handler. With a multithreaded application, nothing prevents another signal of the same type from being delivered to another thread that has the signal unblocked. It is possible to have multiple threads executing within the same signal handler.

A recommended strategy for dealing with signals in multithreaded processes is to dedicate particular threads to signal handling . The main thread blocks all signals before creating the threads. The signal mask is inherited from the creating thread, so all threads have the signal blocked. The thread dedicated to handling the signal then executes sigwait on that signal. (See Section 8.5.) Alternatively, the thread can use pthread_sigmask to unblock the signal. The advantage of using sigwait is that the thread is not restricted to async-signal-safe functions.

Program 13.14 is an implementation of a dedicated thread that uses sigwait to handle a particular signal. A program calls signalthreadinit to block the signo signal and to create a dedicated signalthread that waits for this signal. When the signal corresponding to signo becomes pending, sigwait returns and the signalthread calls setdone of Program 13.3 and returns. You can replace the setdone with any thread-safe function. Program 13.14 has some informative messages, which would normally be removed.

Notice that the implementation of signalthreadinit uses a thread attribute object to create signalthread with higher priority than the default value. The program was tested on a system that used preemptive priority scheduling. When the program executes on this system without first increasing signalthread 's priority, it still works correctly, but sometimes the program takes several seconds to react to the signal after it is generated. If a round- robin scheduling policy were available, all the threads could have the same priority.

The dedicated signal-handling thread, signalthread , displays its priority to confirm that the priority is set correctly and then calls sigwait . No signal handler is needed since sigwait removes the signal from those pending. The signal is always blocked, so the default action for signalnum is never taken.

Program 13.15 modifies computethreadmain of Program 13.7 by using the SIGUSR1 signal to set the done flag for the computethread object of Program 13.6. The main program no longer sleeps a specified number of seconds before calling setdone . Instead, the delivery of a SIGUSR1 signal causes signalthread to call setdone .

Program 13.14 signalthread.c

A dedicated thread that sets a flag when a signal is received .

 #include <errno.h> #include <pthread.h> #include <signal.h> #include <stdio.h> #include "doneflag.h" #include "globalerror.h" static int signalnum = 0; /* ARGSUSED */ static void *signalthread(void *arg) {    /* dedicated to handling signalnum */    int error;    sigset_t intmask;    struct sched_param param;    int policy;    int sig;    if (error = pthread_getschedparam(pthread_self(), &policy, &param)) {       seterror(error);       return NULL;    }    fprintf(stderr, "Signal thread entered with policy %d and priority %d\n",               policy,  param.sched_priority);    if ((sigemptyset(&intmask) == -1)         (sigaddset(&intmask, signalnum) == -1)         (sigwait(&intmask, &sig) == -1))       seterror(errno);    else       seterror(setdone());    return NULL; } int signalthreadinit(int signo) {    int error;    pthread_attr_t highprio;    struct sched_param param;    int policy;    sigset_t set;    pthread_t sighandid;    signalnum = signo;                                    /* block the signal */    if ((sigemptyset(&set) == -1)  (sigaddset(&set, signalnum) == -1)        (sigprocmask(SIG_BLOCK, &set, NULL) == -1))       return errno;    if ( (error = pthread_attr_init(&highprio))     /* with higher priority */         (error = pthread_attr_getschedparam(&highprio, &param))          (error = pthread_attr_getschedpolicy(&highprio, &policy)) )       return error;    if (param.sched_priority < sched_get_priority_max(policy)) {       param.sched_priority++;       if (error = pthread_attr_setschedparam(&highprio, &param))          return error;    } else      fprintf(stderr, "Warning, cannot increase priority of signal thread.\n");    if (error = pthread_create(&sighandid, &highprio, signalthread, NULL))       return error;    return 0; } 
Program 13.15 computethreadsig.c

A main program that uses signalthread with the SIGUSR1 signal to terminate the computethread computation of Program 13.6 .

 #include <math.h> #include <pthread.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include "computethread.h" #include "globalerror.h" #include "sharedsum.h" #include "signalthread.h" int showresults(void); int main(int argc, char *argv[]) {    int error;    int i;    int numthreads;    pthread_t *tids;    if (argc != 2) {                   /* pass number threads on command line */       fprintf(stderr, "Usage: %s numthreads\n", argv[0]);       return 1;    }    if (error = signalthreadinit(SIGUSR1)) {          /* set up signal thread */       fprintf(stderr, "Failed to set up signal thread: %s\n", strerror(error));       return 1;    }    numthreads = atoi(argv[1]);    if ((tids = (pthread_t *)calloc(numthreads, sizeof(pthread_t))) == NULL) {       perror("Failed to allocate space for thread IDs");       return 1;    }    for (i = 0; i < numthreads; i++)      /* create numthreads computethreads */       if (error =  pthread_create(tids+ i, NULL, computethread, NULL)) {          fprintf(stderr, "Failed to start thread %d: %s\n", i,                  strerror(error));          return 1;       }    fprintf(stderr, "Send SIGUSR1(%d) signal to proc %ld to stop calculation\n",                    SIGUSR1, (long)getpid());    for (i = 0; i < numthreads; i++)    /* wait for computethreads to be done */       if (error = pthread_join(tids[i], NULL)) {          fprintf(stderr, "Failed to join thread %d: %s\n", i, strerror(error));          return 1;       }    if (showresults())       return 1;    return 0; } 

The modular design of the signalthread object makes the object easy to modify. Chapter 16 uses signalthread for some implementations of a bounded buffer.

Exercise 13.24

Run computethreadsig of Program 13.15 from one command window. Send the SIGUSR1 signal from another command window, using the kill shell command. What is its effect?

Answer:

The dedicated signal thread calls setdone when the signal is pending, and the threads terminate normally.

Team-FLY


Unix Systems Programming
UNIX Systems Programming: Communication, Concurrency and Threads
ISBN: 0130424110
EAN: 2147483647
Year: 2003
Pages: 274

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net