Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Propagating Signal (SIGINT) to C++11 threads

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.

like image 214
Alexander Tumin Avatar asked Mar 27 '13 11:03

Alexander Tumin


People also ask

How do you handle a SIGINT signal?

We can use signal handling in C for this. When Ctrl+C is pressed, SIGINT signal is generated, we can catch this signal and run our defined signal handler.

Is SIGINT propagated to child processes?

We'll understand why the SIGINT signal is not propagated to the child process. To do that, we need to first understand how Linux handles processes and signals.

What does signal SIGINT Sig_ign do?

signal() function sets the disposition of the signal signum to handler, which is either SIG_IGN, SIG_DFL or the address of a programmer-defined function. If the disposition is set to SIG_IGN, then the signal is ignored. If the disposition is set to SIG_DFL, then the default action associated with the signal occurs.

Can each thread have their own signal handler?

Each thread can have its own set of signals that will be blocked from delivery. The sigthreadmask subroutine must be used to get and set the calling thread's signal mask. The sigprocmask subroutine must not be used in multi-threaded programs; otherwise, unexpected behavior may result.


1 Answers

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;
like image 103
Maxim Egorushkin Avatar answered Sep 24 '22 17:09

Maxim Egorushkin