10

I am trying to terminate correctly my multi-threaded C++11 application upon receiving SIGINT signal (^C it is), but for some reason it does not propagate to child threads, though main thread responds to it well.

For instnce (as in code sample below), if we have a some blocking function inside of thread (like sleep()), it will take out all ^C control from you if you installed any SIGNT hooks using, say, sigaction() function.

Is there any way to fix- or work-around such behavior? Is there a way to propagate main-received signals to child threads?

EDIT: replaced POSIX sleep() with C++11 std::this_thread::sleep_for()

#include <iostream>
#include <thread>
#include <chrono>
#include <signal.h> // for sigaction() function 

static int signaled = 0;

void threaded_foo() {
  while (!signaled) {
    // pressing ^C now will not lead to correct termination, because we are
    // sleeping for a long time (100500 seconds) and do not respond to
    // SIGINT for some tricky reason i am looking a bypassage for.
    std::chrono::seconds duration(100500);
    std::this_thread::sleep_for(duration);
  }
  std::cout << "Correct termination\n";
}

void sighandler(int sig, siginfo_t *siginfo, void *context) {
  signaled = 1;
}

void install_sig_hooks() {
  struct sigaction action;
  memset(&action, 0, sizeof(struct sigaction));
  action.sa_sigaction = sighandler;
  action.sa_flags     = SA_SIGINFO;
  sigaction(SIGINT, &action, NULL);
}

int main(int argc, char **argv) {
  install_sig_hooks();

  std::thread t(threaded_foo);
  t.join();
  return 0;
}

EDIT2 So the solution was to replace all blocking calls with non-blocking counterparts and such mechanisms as condition variables and polling.

The actual question remains unanswered as current software (and, possibly, hardware) is not designed in way of handling signals more then in one thread, which one is supposed to tell others what to do upon receiving signals.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
Alexander Tumin
  • 1,561
  • 4
  • 22
  • 33

1 Answers1

5

I am trying to terminate correctly my multi-threaded C++11 application upon receiving SIGINT signal (^C it is), but for some reason it does not propagate to child threads, though main thread responds to it well.

POSIX distinguishes signals targeted to process or a thread. In either case only one thread receives the signal:

At the time of generation, a determination shall be made whether the signal has been generated for the process or for a specific thread within the process. Signals which are generated by some action attributable to a particular thread, such as a hardware fault, shall be generated for the thread that caused the signal to be generated. Signals that are generated in association with a process ID or process group ID or an asynchronous event, such as terminal activity, shall be generated for the process.

...

... Signals generated for the process shall be delivered to exactly one of those threads within the process which is in a call to a sigwait() function selecting that signal or has not blocked delivery of the signal. If there are no threads in a call to a sigwait() function selecting that signal, and if all threads within the process block delivery of the signal, the signal shall remain pending on the process until a thread calls a sigwait() function selecting that signal, a thread unblocks delivery of the signal, or the action associated with the signal is set to ignore the signal.

In a multi-threaded process a common solution is to block process signals one intends to handle in all threads but one. That one thread would normally handle all process signals and tell other threads what to do (e.g. terminate).

Also, because signaled is set by the signal handler, it should be declared volatile, this is one of the two use cases volatile was introduced for:

static int volatile signaled = 0;
Community
  • 1
  • 1
Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271
  • Your answer arises two new questions: 1) how do you interrupt the thread in C++11, so all sleep()s would wake-up? and 2) how do you actually terminate the thread with C++11 inventory? – Alexander Tumin Mar 27 '13 at 12:10
  • Thanks for the volatile notice, but just for the record the same code works fine with sane sleep duration values, like one second. Though this behavior might be not as well defined. – Alexander Tumin Mar 27 '13 at 12:15
  • 1
    @AlexanderTumin I would recommend against thread interruptions, because 3rd-party libraries you might use may not handle interruptions correctly. Tell your threads politely to terminate and then wait. This assumes that all your threads have some kind of an event loop, so that they can be told to terminate. – Maxim Egorushkin Mar 27 '13 at 12:15
  • The library i am currently using (ZeroMQ) is *intented* to de-block upon receiving SIGINT and return EINTR code for this case. – Alexander Tumin Mar 27 '13 at 12:17
  • 1
    You could replace the sleep() with a timed wait on a condition variable. Then you could broadcast to all the threads when you want them to stop "sleeping." – John Zwinck Mar 27 '13 at 12:18
  • @Maxim Yegorushkin But i guess you're right, i need to use some kind of outgoing polling mechanism in order to set my resources right. Thank you. – Alexander Tumin Mar 27 '13 at 12:18
  • @AlexanderTumin _same code works fine with sane sleep duration_ true, but it is really a lame explanation. Such code introduces uncertainty about its behaviour, these uncertainties pile up and when it crashes or misbehaves you have accrued so much uncertainty you don't have a good guess what might have gone wrong, since it could have been a combination of many uncertainties that caused a crash. – Maxim Egorushkin Mar 27 '13 at 12:19
  • @John Zwinck the sleep is just an example, what would you do in case of blocking read() for instance? – Alexander Tumin Mar 27 '13 at 12:20
  • @Maxim Yegorushkin i know it is lame, as i told i am just recording that missing volatile wasn't the source of my problems. – Alexander Tumin Mar 27 '13 at 12:21
  • @AlexanderTumin: I'd replace the blocking read with a non-blocking one! Blocking is bad. – John Zwinck Mar 27 '13 at 12:24
  • 1
    Using `volatile` is not correct here. See http://www.drdobbs.com/parallel/volatile-vs-volatile/212701484 for why. You should just use `std::atomic` or `std::atomic` for the signaled flag. – Nathan Ernst Mar 27 '13 at 20:11
  • 1
    @NathanErnst See C++ 2003 §1.9.9: _When the processing of the abstract machine is interrupted by receipt of a signal, the values of objects with type other than volatile sig_atomic_t are unspecified, and the value of any object not of volatile sig_atomic_t that is modified by the handler becomes undefined._ To be pedatic here `sig_atomic_t` should be used instead of `int`, however, on Linux platforms `sig_atomic_t` is `int`. – Maxim Egorushkin Mar 27 '13 at 21:29
  • @MaximYegorushkin, I stand corrected about volatile in this context. 2011 §1.9.9 p6 also adds support for lock-free atomic types. – Nathan Ernst Mar 28 '13 at 15:19