Reputation: 390
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
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 tokill(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
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