Obiphil
Obiphil

Reputation: 390

Child thread raising a signal it is ignoring

In an error condition, I want to allow a child thread to raise a signal which is then caught by the parent thread. The other thread then safely terminates the entire process. The issue I see is that if I block the child thread from receiving the signal using pthread_sigmask during its creation, then the signal it raises isn't seen by the other thread. Is this intended behaviour? What can I do about it?

I've checked here and many others like it, but they deal with signals originating externally to the process.

Example code follows, please excuse the use of disallowed functions in the signal handler as it facilitates the example. I used SIGABRT here as it has a very obvious default handler, but the behaviour is the same if SIGUSR1 is used.

#include <unistd.h>
#include <signal.h>
#include <iostream>
#include <thread>

void sigHandler(int )
{
  std::cout << "Caught signal in " << std::this_thread::get_id() << std::endl;
}

void threadMain()
{
  std::cout << "Child is " << std::this_thread::get_id() << std::endl;
  sleep(1);
  raise(SIGABRT);
  std::cout << "Child exiting" << std::endl;
}

int main(int argc, char** argv)
{
  std::cout << "Parent is " << std::this_thread::get_id() << std::endl;
  struct sigaction psa;
  psa.sa_handler = sigHandler;
  sigaction(SIGABRT, &psa, nullptr);
  sigset_t set;
  sigemptyset(&set);
  sigaddset(&set, SIGABRT);
  if (argc > 1)
  {
    if (pthread_sigmask(SIG_BLOCK, &set, NULL) == 0)
      std::cout << "Masking complete" << std::endl;
    else
      std::cout << "Failed to mask" << std::endl;
  }
  std::thread child(threadMain);
  if (argc > 1)
  {
    if (pthread_sigmask(SIG_UNBLOCK, &set, NULL) == 0)
      std::cout << "Unmask complete" << std::endl;
    else
      std::cout << "Failed to mask" << std::endl;
  }
  sleep(2);
  child.join();
}

If you compile with

g++ --std=c++11 -lpthread main.cpp -g -o main

and run with ./main the output is:

Parent is 140042782496544
Child is 140042766427904
Caught signal in 140042766427904
Child exiting

So the signal was caught, but by the child (not the parent, like I wanted). If you run it with ./main 1 the output is:

Parent is 140070068569888
Masking complete
Unmask complete
Child is 140070052501248
Child exiting

i.e. the signal was blocked so the child didn't see it (which is what I wanted) but the parent didn't see it either.

How can I make sure the signal is raised by the child and tended to by the parent?

Upvotes: 2

Views: 1523

Answers (2)

ilkkachu
ilkkachu

Reputation: 6527

The manual for raise(3) seems to indicate that it sends the signal specifically to the calling thread, not the whole process, so a signal raised by one thread will not be seen by the other:

The raise() function sends a signal to the calling process or thread. In a single-threaded program it is equivalent to

      kill(getpid(), sig);

In a multithreaded program it is equivalent to

      pthread_kill(pthread_self(), sig);

Changing the raise(SIGABRT) explicitly to kill(getpid(), SIGABRT) seems to do what you want, and send the signal to the whole process, so it's caught by some thread that does not have it blocked.

Upvotes: 4

UKMonkey
UKMonkey

Reputation: 6993

Sounds to me like you want raise() to have thread communications in - well, it doesn't. So what you want to do isn't possible directly.

You can however set a variable, or use any other thread communications in your handler to report to your main thread that the signal was raised.

Alternatively, you could use pthread_kill...

Upvotes: 1

Related Questions