POSIX threads and signals

CPthreadsSignals

C Problem Overview


I've been trying to understand the intricacies of how POSIX threads and POSIX signals interact. In particular, I'm interested in:

  • What's the best way to control which thread a signal is delivered to (assuming it isn't fatal in the first place)?
  • What is the best way to tell another thread (that might actually be busy) that the signal has arrived? (I already know that it's a bad idea to be using pthread condition variables from a signal handler.)
  • How can I safely handle passing the information that a signal has occurred to other threads? Does this need to happen in the signal handler? (I do not in general want to kill the other threads; I need a far subtler approach.)

For reference about why I want this, I'm researching how to convert the TclX package to support threads, or to split it up and at least make some useful parts support threads. Signals are one of those parts that is of particular interest.

C Solutions


Solution 1 - C

> * What's the best way to control which thread > a signal is delivered to?

As @zoli2k indicated, explicitly nominating a single thread to handle all signals you want handled (or a set of threads each with specific signal responsibilities), is a good technique.

> * What is the best way to tell another thread (that might actually be busy) > that the signal has arrived?[...] > * How can I safely handle passing the information that a signal has occurred > to other threads? Does this need to happen in the signal handler?

I won't say "best," but here's my recommendation:

Block all desired signals in main, so that all threads are inherit that signal mask. Then, fashion the special signal receiving thread as a signal-driven event loop, dispatching newly arrived signals as some other intra-thread communication.

The simplest way to do this is to have the thread accept signals in a loop using sigwaitinfo or sigtimedwait. The thread then converts the signals somehow, perhaps broadcasting a pthread_cond_t, waking up other threads with more I/O, enqueuing a command in an application-specific thread-safe queue, whatever.

Alternatively, the special thread could allow signals to be delivered to a signal handler, unmasking for delivery only when ready to handle signals. (Signal delivery via handlers tends to be more error-prone than signal acceptance via the sigwait family, however.) In this case, the receiver's signal handler performs some simple and async-signal-safe action: setting sig_atomic_t flags, calling sigaddset(&signals_i_have_seen_recently, latest_sig), write() a byte to a non-blocking self-pipe, etc. Then, back in its masked main loop, the thread communicates receipt of the signal to other threads as above.

(UPDATED @caf rightly points out that sigwait approaches are superior.)

Solution 2 - C

According to the POSIX standard all threads should appear with the same PID on the system and using pthread_sigmask() you can define the signal blocking mask for every thread.

Since it is allowed to define only one signal handler per PID, I prefer to handle all signals in one thread and send pthread_cancel() if a running thread need to be cancelled. It is the preferred way against pthread_kill() since it allows to define cleanup functions for the threads.

On some older systems, because of the lack of proper kernel support, the running threads may have different PID from the parent thread's PID. See FAQ for signal handling with linuxThreads on Linux 2.4.

Solution 3 - C

Where I'm at so far:

  • Signals come in different major classes, some of which should typically just kill the process anyway (SIGILL) and some of which never need anything doing (SIGIO; easier to just do async IO right anyway). Those two classes need no action.
  • Some signals don't need to be dealt with immediately; the likes of SIGWINCH can be queued up until it is convenient (just like an event from X11).
  • The tricky ones are the ones where you want to respond to them by interrupting what you're doing but without going to the extent of wiping out a thread. In particular, SIGINT in interactive mode ought to leave things responsive.

I've still got to sort through signal vs sigaction, pselect, sigwait, sigaltstack, and a whole bunch of other bits and pieces of POSIX (and non-POSIX) API.

Solution 4 - C

IMHO, Unix V signals and posix threads do not mix well. Unix V is 1970. POSIX is 1980 ;)

There are cancellation Points and if you allow signals and pthreads in one application, you will eventually end up writing Loops around each call, which can surprisingly return EINTR.

So what I did in the (few) cases where I had to program multithreaded on Linux or QNX was, to mask out all signals for all (but one) threads.

When a Unix V Signal arrives, the process Switches the stack (that was as much concurrency in Unix V as you could get within a process).

As the other posts here hint, it might be possible now, to tell the System, which posix thread shall be the victim of that stack switching.

Once, you managed to get your Signal handler thread working, the question remains, how to transform the signal information to something civilized, other threads can use. An infrastructure for inter-thread communications is required. One pattern, useful is the actor pattern, where each of your threads is a target for some in-process Messaging mechanism.

So, instead of canceling other threads or killing them (or other weird stuff), you should try to marshall the Signal from the Signal context to your Signal handler thread, then use your actor pattern communications mechanisms to send semantically useful messages to those actors, who need the signal related Information.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDonal FellowsView Question on Stackoverflow
Solution 1 - CpilcrowView Answer on Stackoverflow
Solution 2 - Czoli2kView Answer on Stackoverflow
Solution 3 - CDonal FellowsView Answer on Stackoverflow
Solution 4 - Cuser2173833View Answer on Stackoverflow