views:

483

answers:

3

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.

+4  A: 

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 pthreads_cancel if a running thread need to be cancelled. It is the preferred way against pthreads_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.

zoli2k
In what you say “implemented” means what? Also, it is not correct to always nuke other threads in response to a signal (SIGHUP and SIGWINCH require more subtlety) and yet it is unsafe to use condition variables for letting other threads know. Poor answer.
Donal Fellows
@Donal Fellows > Post edited. I hope it is more helpful now.
zoli2k
@zoli2k: Removed my down-vote, but it's still not a sufficient answer because I can't just kill threads in response to a signal. In some cases I'm going to be queueing events locally in response, in others I've got to tear down threads very carefully (BTW, I've got most of the machinery to do those parts already; it's the connections to the OS signals that are missing).
Donal Fellows
+1  A: 

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.

Donal Fellows
+3  A: 
  • 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:

Fashion the signal receiving thread as a signal-driven event loop, dispatching newly arrived signals as some other intra-thread communication. This thread will block all signals except when waiting for new ones to arrive.

The receiver's signal handler is simple and uncontroversial. It might, for instance:

  • set sig_atomic_t flags
  • call sigaddset(&signals_i_have_seen_recently, latest_sig)
  • write() a byte to a non-blocking self-pipe to break itself out of a select or pselect
    (or to awaken another thread directly)

all of which are signal handler safe.

In the loop body, again with most/all signals blocked, the thread then converts the newly arrived signals, 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.

pilcrow
That's a much more useful answer, especially as it can be used for handling non-fatal signal handling too. Thanks!
Donal Fellows