signal handler function in multithreaded environment
Asked Answered
K

3

17

In my multithreaded GUI application I have the following signal handling code. I want to improve this code so that it will be correct and threading safe but there are some things I don't fully understand in signal handling:

  • is signal handled at the process or thread level (can I have thread-specific signal handlers) ?
  • in which thread context is signal_handler function executed ?
  • is it possible to send many SIGTERM signals in a short time ?
  • does it make sense to use a mutex to prevent parallel execution of signal_handler ?

void signal_handler(int sig)
{
        switch (sig)
        {
        case SIGTERM:
            ::wxLogMessage(wxT("SIGTERM signal received ..."));
            break;
        case SIGINT:
            ::wxLogMessage(wxT("SIGINT signal received ..."));
            break;
        case SIGUSR1:
            ::wxLogMessage(wxT("SIGUSR1 signal received ..."));
            break;
        default:
            ::wxLogMessage(wxT("Unknown signal received ..."));
        }

        // send wxCloseEvent to main application window
        ::wxGetApp().GetTopWindow()->Close(true);
}

I register signal handlers in my init function:

// register signal handlers
signal(SIGTERM, signal_handler);
signal(SIGINT,  signal_handler);
signal(SIGUSR1, signal_handler);
Kristofor answered 18/10, 2012 at 10:13 Comment(0)
R
15

Be very careful: as the signal(7) page is telling, only very few functions (the "async-signal-safe" ones, see signal-safety(7) for more) can be (directly or indirectly) called inside signal handlers. Mutex related functions probably should not be called in signal handlers. See also pthreads(7)

You might consider setting a volatile sigatomic_t variable in your signal handler, and test the value of that flag from time to time. If you have C++11 (or C11) atomics, e.g. C++11 std::atomic or C11 <stdatomic.h>, you could make that volatile variable also atomic in that sense. Then use the atomic load facilities to test it.

The Qt documentation suggests the following trick: create a pipe(2) to self at startup, then have your signal handler write(2) (the write syscall is specified as being async-signal-safe) a single (or more) byte[s] to a pipe to your same process, and have your GUI event loop poll(2) the read end of that pipe.

A Linux-specific way to handle signals with Qt might be to use signalfd(2) probably with QSocketNotifier (despite the name, it works on pollable file descriptors, not only sockets). With other GUI toolkits, you probably can also add a file descriptor (the one from signalfd or pipe) to be polled.

Remitter answered 18/10, 2012 at 10:18 Comment(1)
Note use of std::atomic in signal handlers doesn't guarantee lock-free implementations. Please see: informit.com/articles/article.aspx?p=2204014Pinkard
D
28
  • Signal handlers are per-process state - that is, all the threads in a process share the same set of installed signal handler functions.
  • Signal masks are per-thread state. Signals can be blocked or unblocked on a per-thread basis.
  • Signals can be process- or thread-directed. If a signal is process-directed, then an arbitrary thread which does not currently have the signal type blocked is chosen to handle it.

A simple way to handle signals in a multi-threaded application is to create one thread as a dedicated signal-handling thread. All signals of interest are blocked in every thread; no signal handlers are established; and the signal-handling thread calls sigwaitinfo() in a loop, acting on the signals as they're received.

This means that you don't need to worry about whether the functions you want to call are async-signal-safe or not, because signals aren't handled in signal handlers - they're handled synchronously by your dedicated signal-handling thread, which can call any function it likes (for example, it can use the ordinary pthreads synchronisation functions to wake up another thread).

Devondevona answered 18/10, 2012 at 11:2 Comment(1)
I used this method in one of my projects, and it worked wellReynaud
R
15

Be very careful: as the signal(7) page is telling, only very few functions (the "async-signal-safe" ones, see signal-safety(7) for more) can be (directly or indirectly) called inside signal handlers. Mutex related functions probably should not be called in signal handlers. See also pthreads(7)

You might consider setting a volatile sigatomic_t variable in your signal handler, and test the value of that flag from time to time. If you have C++11 (or C11) atomics, e.g. C++11 std::atomic or C11 <stdatomic.h>, you could make that volatile variable also atomic in that sense. Then use the atomic load facilities to test it.

The Qt documentation suggests the following trick: create a pipe(2) to self at startup, then have your signal handler write(2) (the write syscall is specified as being async-signal-safe) a single (or more) byte[s] to a pipe to your same process, and have your GUI event loop poll(2) the read end of that pipe.

A Linux-specific way to handle signals with Qt might be to use signalfd(2) probably with QSocketNotifier (despite the name, it works on pollable file descriptors, not only sockets). With other GUI toolkits, you probably can also add a file descriptor (the one from signalfd or pipe) to be polled.

Remitter answered 18/10, 2012 at 10:18 Comment(1)
Note use of std::atomic in signal handlers doesn't guarantee lock-free implementations. Please see: informit.com/articles/article.aspx?p=2204014Pinkard
P
5

This answer refers to POSIX threads (pthreads).

Referring 1:

Signals could be handled on thread level, yes. If more than one thread of a process handles a signal and the signal ist sent to the process, but to a specific thread it is not determined which thread's handler will handle the signal. (see man pthread_kill() for details)

Referring 2:

The signal handler will be excuted in the context of the thread which set it. This includes the main thread.

Referring 3:

If more than one signal of the same type is sent to the same process they might be condensed in into only one signal before leaving the signal queue. Whether this could be differentiate to the thread level I do not know for sure, I have to admit.

Referring 4:

If shared resources are involved in the game: yes, at least for the parts of the handlers' code accessing those resources concurrently. And moreover this also depends on the logic you try to implement.

Psychopharmacology answered 18/10, 2012 at 10:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.