9

In my applications, I generally want to intercept SIGINT and SIGTERM signals in order to close down gracefully.

In order to prevent worker threads from "stealing" signals, I do this in the entrypoint for each:

// Block signals in this thread
sigset_t signal_set;
sigaddset(&signal_set, SIGINT);
sigaddset(&signal_set, SIGTERM);
sigaddset(&signal_set, SIGHUP);
sigaddset(&signal_set, SIGPIPE);
pthread_sigmask(SIG_BLOCK, &signal_set, NULL);

If I don't, when I perform Ctrl+C, some of the time (it's unspecified as to which thread will get the signal) my handlers in the base thread won't be invoked — instead, the signal just terminates the process from within the worker thread. This is obviously not cool.

So I have one signal-handling thread and block signals everywhere else.

However, I don't notice anybody else doing this, it's easy enough to forget to do, and it's also not entirely portable. Is there some more simple trick I'm missing?


References:

Community
  • 1
  • 1
Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • do you do the signal blocking in the parent thread before `pthread_create`, or in each newly created ones? – didierc Nov 21 '12 at 17:10
  • @didierc: At the top of the "entrypoint" function for each newly created thread. Though, as it happens, there's one in the parent thread too. Only one thread -- which is designated as a signal-handling thread -- doesn't have these lines. – Lightness Races in Orbit Nov 21 '12 at 17:13
  • Oh right. Well I suppose that some people may do like you, some would run a dedicated thread, other would perhaps ignore the issue completely. I don't think that there's a general pattern. Other languages might sidestep the problem by providing more "high level" mechanisms like event listeners, threadpools, tasks and so on, which take care of the problem. Maybe the C++ world has a more barebone approach, hence the absence of a general consensus. – didierc Nov 21 '12 at 17:27
  • What do you mean by "not entirely portable"? – didierc Nov 21 '12 at 17:31
  • @didierc: It breaks the abstraction provided by _boost.thread_ by relying on pthreads. Of course I could provide a Windows alternative myself, too, but this is already a fair amount of boilerplate. Also I don't really know how close to this signals work in Windows. – Lightness Races in Orbit Nov 21 '12 at 17:31
  • Also, there could be situations where individual signals would be better caught in specific threads perhaps? – didierc Nov 21 '12 at 17:33
  • I see. How about boost::Asio::signal_set? I suppose that the framework doesn't block the signals on each thread to begin with, so you have to do it by yourself? – didierc Nov 21 '12 at 17:45
  • @didierc: If by framework you mean Boost's threading mechanism, no it doesn't do that. If you mean the underlying framework in my own code, it does do that for threads instantiated through its API, but there's still nothing stopping someone from just constructing their own `boost::thread` on a whim. I can't find anything hugely pertinent in [`boost::asio::signal_set`](http://www.boost.org/doc/libs/1_52_0/doc/html/boost_asio/overview/signals.html). – Lightness Races in Orbit Nov 21 '12 at 17:50
  • Basically, if threads should be reacting to certain (system) events, shouldn't that event system been thrown in as a template parameter somehow? The boost philosophy seems to let users cherry pick their favorite tools, but at the same time, I feel that there should be some form of cohesion between the tools (just like the STL does with allocators for instance). Maybe the boost people are already thinking about something there. – didierc Nov 21 '12 at 18:06
  • @LightnessRacesinOrbit Could you give some information on how you manage the worker threads once you have caught the signal in the signal handler thread? I don't know what I should with my threads. Should I just `exit(1)`? – Ali Aug 05 '16 at 04:14
  • 1
    @Ali: The signal handling thread sets a flag that's picked up by the main thread, that then interrupts and joins all the worker threads. With `boost::thread` that's as simple as `thread.interrupt(); thread.join();` with some `boost::interruption_points` scattered around the worker thread code (or, just wait for the threads to finish their work); with `std::thread` you'll need an `std::atomic interruptThreads` to set and check. – Lightness Races in Orbit Aug 05 '16 at 09:43

1 Answers1

5

I find it a perfectly reasonable thing to do.

You could block the signals in main, before any other thread is spawned. The spawned threads will inherit the creator thread signal mask and you can unblock signals only in the signal handling thread (being careful only if that thread spawns other threads as well).

Or you can leave the signals blocked everywhere and explicitly handle them via sigwait and friends in the signal handling thread.

chill
  • 16,470
  • 2
  • 40
  • 44
  • Oh, that's interesting - I didn't know the mask would be inherited. I do set it in the parent thread but not until the start of an event loop, by which time some components have already been created and spawned threads from their constructors, resulting in the behaviour I've seen. If I move the parent thread signal blocking to the constructor of `kernel` (an object which must be constructed before any components), then this will be much simpler. Excellent! – Lightness Races in Orbit Nov 21 '12 at 22:38