Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

POSIX threads and signals

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.

like image 318
Donal Fellows Avatar asked Apr 04 '10 16:04

Donal Fellows


People also ask

What is a POSIX signal?

SIGABRT. The SIGABRT signal is sent to a process to tell it to abort, i.e. to terminate. The signal is usually initiated by the process itself when it calls abort function of the C Standard Library, but it can be sent to the process from outside like any other signal.

How do Posix threads work?

The POSIX thread libraries are a C/C++ thread API based on standards. It enables the creation of a new concurrent process flow. It works well on multi-processor or multi-core systems, where the process flow may be scheduled to execute on another processor, increasing speed through parallel or distributed processing.

Do threads receive signals?

A process-directed signal may be delivered to any one of the threads that does not currently have the signal blocked. If more than one of the threads has the signal unblocked, then the kernel chooses an arbitrary thread to which to deliver the signal.

Are signals per thread or per process?

signal is a per process call, not a per thread one, if you call it it sets the handler for all threads in the process. Signals and threads is a complex topic. You might be better off asking about what you really what to do. Using signal in a multithreaded program is essentially undefined behaviour.


2 Answers

  • 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.)

like image 145
pilcrow Avatar answered Sep 23 '22 14:09

pilcrow


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.

like image 28
zoli2k Avatar answered Sep 21 '22 14:09

zoli2k