Threads and signals

When a process receives a signal of some type (for example, SIGINT type) the process can either take the default response, ignore the signal (the kernel does not actually deliver the signal), or catch the signal. When the signal is caught, the system will call a handler function when the signal is delivered. This response is called the ``disposition'' for the signal type. In SVR4.2MP and SCO OpenServer, that disposition is common to all of the threads of a process.

If the disposition for a signal type is

Such signals will terminate all threads, and the process will terminate.

Such signals will be ignored by all threads.

Any thread responding to such signals will enter the same handler function.

Moreover, if any thread changes the disposition (say by calling sigaction(S)) the new disposition is in effect for all threads.

NOTE: System signal types SIGLWP and SIGWAITING are used internally by the Threads Library. The Threads Library prevents modification of the disposition or masking of those signal types.

On the other hand, ``signal masks'' (the set of signal types being blocked) are maintained per thread. Signal masking for threads behaves identically to the traditional signal masking for processes. If the disposition for a particular signal type is not ``ignore'' and the signal is not masked for a thread targeted to receive that signal, the signal will be delivered and the appropriate action will be taken by the thread. If the disposition is not ``ignore'' but the signal is masked by a thread targeted to receive that signal, the signal will be made ``pending'' for the thread; this means that the thread will not take the appropriate action until it unmasks that signal. If the disposition of the signal is ``ignore,'' the signal is neither made pending nor delivered, regardless of the thread's signal mask.

A thread inherits the signal mask of its creating thread. A thread can alter its mask with the thr_sigsetmask(S) routine.

   int thr_sigsetmask(
   	int	         how,
   	const sigset_t *set,
   	      sigset_t *oset

NOTE: The syntax of thr_sigsetmask(S) is nearly identical to that of the sigprocmask(S) system call.

Signals can be categorized as being asynchronously generated or synchronously generated. A ``synchronously-generated signal'' is one that arises from the action of a particular thread or process. For example, alarm signals, signals resulting from an illegal memory reference, and signals resulting from an illegal arithmetic operation are all synchronously-generated signals. An ``asynchronously-generated'' signal is one that is sent from outside the thread (or process); its delivery is unpredictable. Interruptions and termination signals are usually an asynchronously generated.

Asynchronously-generated signals

When a signal is delivered to a process, if it is being caught, it will be handled by one, and only one, of the threads meeting either of the following conditions:

  1. A thread blocked in a sigwait(S) system call whose argument does include the type of the caught signal.

  2. A thread whose signal mask does not include the type of the caught signal.
Additional considerations:

Asynchronously-generated signals -- paradigm

One useful paradigm for managing signals originating outside of the process is to have all threads include the caught signals in their signal mask and specifically create one daemon thread to handle the signals. If that thread uses the sigwait(S) system call, the signals can be handled in a synchronous style.

   while( (signo = sigwait(mask)) > 0){
   	handle signal type signo

Note that it is not only valid to wait for masked signals with sigwait, but it is important to mask out the signal types of interest before calling sigwait. Otherwise, the arrival of one such signal between calls to sigwait will be handled according to the current process disposition. By default, that will terminate the entire process. sigwait effectively unmasks any masked signals while blocked, then masks them again before returning.

Even if a handler function is specified, it will not be executed if a signal is delivered to a thread blocked in sigwait; sigwait bypasses any handler.

Since all threads are masking out the same set of signals, one can predict that the signals in that set will be handled by the single thread using sigwait. This paradigm is advantageous because:

NOTE: The thread that handles the signals should be a ``bound'' thread. Bound threads are introduced in ``Managing threads concurrency''.

Synchronously-generated signals

A caught, non-masked signal that is caused by a particular thread will be handled by that thread. Examples include:

Each thread will use the common handler function.

Thread-to-thread signaling

One thread can signal another thread with the thr_kill(S) function:

   int thr_kill(
   	thread_t tid,
   	int      signo

The thread ID of the target thread.

The type of signal to send.

A thread catching a signal cannot distinguish between a signal originating from another thread of the process or from outside of the process.

The process disposition for the sent signal type (signo) is also applied for thread-to-thread signaling. As usual, the response will be to ignore the signal, to call the handler function, or to take the default response (usually, process termination).

This facility allows one thread to influence (perhaps ``reset'' or terminate) another thread asynchronously.

Next topic: Lightweight processes and threads concurrency level
Previous topic: Thread-specific data

© 2005 The SCO Group, Inc. All rights reserved.
SCO OpenServer Release 6.0.0 -- 02 June 2005