Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I send signals to different threads

Tags:

c++

c

linux

x86

gcc

Say I have a Thread A, which wants to send signals to Threads B, C and D. Can I do something like this.

SendSignalTo( ThreadB, SIGUSR1 );
SendSignalTo( ThreadC, SIGUSR1 );
SendSignalTo( ThreadD, SIGUSR1 );

With SIGUSR1 signal handler, defined differently for Thread B, C and D, like as follows.

void SIGUSR1_Handler_for_ThreadB(...){...}
void SIGUSR1_Handler_for_ThreadC(...){...}
void SIGUSR1_Handler_for_ThreadD(...){...}

And if not, what is the alternative.

like image 912
pythonic Avatar asked Jan 15 '23 08:01

pythonic


1 Answers

You can send a signal to a specific thread using pthread_kill(), or if you don't mind being GNU-specific, using pthread_sigqueue() (which can specify one int or void * the handler can access via info->si_value).

There is only one signal handler per signal for the process. This means that a specific signal will always call the same handler function, no matter which thread it happens to be. If one thread sets a new signal handler, the signal handler will change for all threads.

However, the workaround is trivial: use a per-thread function pointer to define which function the signal handler should call. Just remember the signal handler limitations -- you can only use async-signal safe functions in a signal handler.

/* Simplify by defining the signal handler function type, assume SA_SIGINFO */
typedef void (*signal_handler_t)(int, siginfo_t *, void *);

/* Per-thread variable pointing to the desired function */
static __thread signal_handler_t  thread_handler = NULL;

/* Process-wide actual signal handler */
static void signal_handler(int signum, siginfo_t *info, void *context)
{
    signal_handler_t  func;

    func = __sync_fetch_and_or(&thread_handler, (signal_handler_t)0);

    if (func)
            func(signum, info, context);
}

The atomic load (__sync_fetch_and_or()) allows you to trivially change the per-thread handler using a simple atomic store at any point in time, without even blocking the signal. Switching to function new_thread_handler is then

    signal_handler_t  func;

    do {
        func = thread_handler;
    } while (!__sync_bool_compare_and_swap(&thread_handler, func, new_thread_handler));

The __sync_fetch_and_or() and the function switch can both be replaced by one C++11-style __atomic_ call, but I don't have a GCC recent enough yet, so I'm still using the old-style __sync_ calls.

POSIX also supports real-time signals, SIGRTMIN+0, SIGRTMIN+1, .., SIGRTMAX. They have the added benefit that more than one of them can be pending at the same time. They are much better suited for this kind of thing than the traditional signals.

like image 53
Nominal Animal Avatar answered Jan 28 '23 12:01

Nominal Animal