Search code examples
c++linuxmultithreadingpthreadssignals

Using mutexes in signal handers


I have a multithreaded application and need to stop it gracefully on interrupt, termination, etc. signals.

Here goes a snippet to illustrate the relevant parts of the logic.

std::atomic_bool running = true;
std::mutex mutex;
std::condition_variable condition;

void signal_handler(int signal)
{
  // in another thread `[&]() { return !running || something_to_do_conditon; } == false`
  // what means continue to wait
  running = false;
  condition.notify_one();
  // another thread goes to sleep
}

void run() {
    while(true) {
      std::unique_lock lock(mutex);
      condition.wait_for(lock, std::chrono::hours(1), [&]() { return !running || something_to_do_conditon; });

      if (!running) {
          return;
      }

      // do smth
    }
}

int main()
{
  // Install a signal handler
  std::signal(SIGINT, signal_handler);
  std::signal(SIGTERM, signal_handler);

  std::thread thread(run);
  thread.join();
}

As you can see in the signal_handler the might be situation where even running is set to false, condition is notified there is still a scenario (described with the in-line comments) where the thread goes to sleep for 1 hour. This happens because there is no mutex around the running variable. That allows the thread to lock the mutex and check the condition right before the variable is set the variable is set. If I add something like

  {
      std::lock_guard<std::mutex> lock(mutex);
      running = false;
  }

in the handler that would be avoided.

Then the question is how to use (is it possible at all) mutexes without getting potential deadlock or any other problems. Any other tricks to weak up a sleeping thread from a signal.


Solution

  • A reliable way to handle signals in a pthreads program is to mask all the signals you wish to handle in every thread, and create a dedicated signal handling thread that loops around calling sigwaitinfo() (or sigtimedwait()).

    The signal handling thread can then use ordinary mutex-protected shared variables and pthread_cond_signal() / pthread_cond_broadcast() wakeups to inform other threads about the signals received.

    In your example, if written in this way the dedicated signal handling thread can safely lock the mutex before changing the running flag, since it is just in ordinary thread context and not a signal handler.