I\'m having trouble getting threads to catch the correct signals.
For example,
I first start a main thread (tid 1).
Then, it sets a signal handler f
It is not possible to install "per-thread" signal handlers.
From man 7 signal (emphasis by me):
The signal disposition is a per-process attribute: in a multithreaded application, the disposition of a particular signal is the same for all threads.
It however is possible to direct each signal-type to a different thread, by masking out the reception of any number of signal-types on a "per-thread" base.
On how to direct a set of signal-types to a specific thread you might like to have look at this answer: https://stackoverflow.com/a/20728819/694576
In addition to alk's answer:
You can use a per-thread function pointer to choose which function is executed when a certain signal is delivered, on a per-thread manner.
Note: Signals are delivered to any thread that is not explicitly blocking its delivery. This does not change that. You still need to use pthread_kill()
or similar mechanisms to direct the signal to a specific thread; signals that are raised or sent to the process (instead of a specific thread), will still be handled by a random thread (among those that do not block it).
I cannot think of any use case where I'd personally prefer this approach; thus far there has always been some other way, something else easier and better. So, if you are considering implementing something like this for a real application, please step back and reconsider your application logic.
But, since the technique is possible, here's how I might implement it:
#include <signal.h>
/* Per-thread signal handler function pointer.
* Always use set_thread_SIG_handler() to change this.
*/
static __thread void (*thread_SIG_handler)(int, siginfo_t *, void *) = (void *)0;
/* Process-wide signal handler.
*/
static void process_SIG_handler(int signum, siginfo_t *info, void *context)
{
void (*func)(int, siginfo_t *, void *);
#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)
func = __atomic_load_n(&thread_SIG_handler, __ATOMIC_SEQ_CST);
#else
func = __sync_fetch_and_add(&thread_SIG_handler, (void *)0);
#endif
if (func)
func(signum, info, context);
}
/* Helper function to set new per-thread signal handler
*/
static void set_thread_SIG_handler(void (*func)(int, siginfo_t *, void *))
{
#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)
__atomic_store_n(&thread_SIG_handler, func, __ATOMIC_SEQ_CST);
#else
void (*oldfunc)(int, siginfo_t *, void *);
do {
oldfunc = thread_SIG_handler;
} while (!__sync_bool_compare_and_swap(&thread_SIG_handler, oldfunc, func));
#endif
}
/* Install the process-wide signal handler.
*/
int install_SIG_handlers(const int signum)
{
struct sigaction act;
sigemptyset(&act.sa_mask);
act.sa_sigaction = process_SIG_handler;
act.sa_flags = SA_SIGACTION;
if (sigaction(signum, &act, NULL))
return errno;
return 0;
}
I like the above because it does not require pthreads, and is very robust and reliable. Aside from the visual mess due to having that preprocessor logic to select which style of atomic built-ins are used, it's very simple too, if you look at it carefully.
GCC 4.7 and later provide C++11-like __atomic built-ins, older GCC versions and other compilers (ICC, Pathscale, Portland Group) provide __sync legacy built-ins. The __thread keyword for thread-local storage should similarly be available in all current POSIX-y systems.
If you have an archaic system, or insist on standards compliance, the following code should have roughly equivalent behaviour:
#include <pthread.h>
#include <signal.h>
#include <errno.h>
static pthread_key_t thread_SIG_handler_key;
static void process_SIG_handler(int signum, siginfo_t *info, void *context)
{
void (*func)(int, siginfo_t *, void *);
*((void **)&func) = pthread_getspecific(thread_SIG_handler_key);
if (func)
func(signum, info, context);
}
static int set_thread_SIG_handler(void (*func)(int, siginfo_t *, void *))
{
sigset_t block, old;
int result;
sigemptyset(&block);
sigaddset(&block, SIG); /* Use signal number instead of SIG! */
result = pthread_sigmask(SIG_BLOCK, &block, &old);
if (result)
return errno = result;
result = pthread_setspecific(thread_SIG_handler_key, (void *)func);
if (result) {
pthread_sigmask(SIG_SETMASK, &old, NULL);
return errno = result;
}
result = pthread_sigmask(SIG_SETMASK, &old, NULL);
if (result)
return errno = result;
return 0;
}
int install_SIG_handlers(const int signum)
{
struct sigaction act;
int result;
result = pthread_key_create(&thread_SIG_handler_key, NULL);
if (result)
return errno = result;
sigemptyset(&act.sa_mask);
act.sa_sigaction = process_SIG_handler;
act.sa_flags = SA_SIGACTION;
if (sigaction(signum, &act, NULL))
return errno;
return 0;
}
I think the closest real-life equivalent to code like this that I've actually ever used, is one where I used one realtime signal (SIGRTMIN+0
) blocked in all but one thread, as a reflector: it sent another realtime signal (SIGRTMIN+1
) to a number of worker threads, in order to interrupt blocking I/O. (It is possible to do this with a single realtime signal, but the two-signal model is simpler to implement and easier to maintain.)
Such signal reflection or fanout is sometimes useful, and it's not that different from this approach. Different enough to warrant its own question, though, if someone is interested.