Throwing an exception from within a signal handler
We have a library that deals with many aspects of error reporting. I have been tasked to port this library to Linux. When running though my little test suite, one of the tests failed. A simplified version of the test appears below.
// Compiler: 4.1.1 20070105 RedHat 4.1.1-52
// Output: Terminate called after throwing an instance of 'int' abort
#include <iostream>
#include <csignal>
using namespace std;
void catch_signal(int signalNumber)
{
signal(SIGINT, SIG_DFL);
throw(signalNumber);
}
int test_signal()
{
signal(SIGINT, catch_signal);
try
{
raise(SIGINT);
}
catch (int &z)
{
cerr << "Caught exception: " << z << endl;
}
return 0;
}
int main()
{
try
{
test_signal();
}
catch (int &z)
{
cerr << "Caught unexpected exception: " << z << endl;
}
return 0;
}
My expectation is that the Caught exception: message will be displayed. What actually happens is that the program terminates as no catch handler appears to be present for the thrown int.
There are a few questions on SO that seem related. I found a number of Google pages that were related. The 'wisdom' seems to boil down to.
- Ya can't throw exceptions from signal handlers, cause the signal handler runs with its own stack, so there are no handlers defined on it.
- Ya can throw exceptions from signal handlers, just reconstruct a fake frame on the stack, and you are good to go.
- Ya, we do it all the time. It works for me on platform X
-
Ya, that used to be available with gcc, but doesn't seem to work any more. Try the -fnon-call-exceptions option, maybe that will work
The code works as expected on our AIX/TRU64/MSVC compiler/environments. It fails in our Linux environment.
I am looking for suggestions to help resolve this issue so the library behavior on Linux will match my other platforms, or some sort or workaround that might achieve the same sort of functionality.
Letting the program core dump on signal, is not a viable option.
Solution 1:
Signals are totally different than C++ exceptions. You can't use a C++ try/catch block to handle a signal. Specifically, signals are a POSIX concept, not a C++ language concept. Signals are delivered asynchronously to your application by the kernel, whereas C++ exceptions are synchronous events defined by the C++ standard.
You are quite limited in what you can do portably in a POSIX signal handler. A common strategy is to have a global flag of type sig_atomic_t
which will be set to 1 in the signal handler, and then possibly longjmp
to the appropriate execution path.
See here for help writing proper signal handlers.
Solution 2:
This code demonstrates a technique which moves the throwing of the exception out of the signal handler into the code. My thanks to Charles for the idea.
#include <iostream>
#include <csignal>
#include <csetjmp>
using namespace std;
jmp_buf gBuffer; // A buffer to hold info on where to jump to
void catch_signal(int signalNumber)
{
//signal(SIGINT, SIG_DFL); // Switch to default handling
signal(SIGINT, catch_signal); // Reactivate this handler.
longjmp // Jump back into the normal flow of the program
(
gBuffer, // using this context to say where to jump to
signalNumber // and passing back the value of the signal.
);
}
int test_signal()
{
signal(SIGINT, catch_signal);
try
{
int sig;
if ((sig = setjmp(gBuffer)) == 0)
{
cout << "before raise\n";
raise(SIGINT);
cout << "after raise\n";
}
else
{
// This path implies that a signal was thrown, and
// that the setjmp function returned the signal
// which puts use at this point.
// Now that we are out of the signal handler it is
// normally safe to throw what ever sort of exception we want.
throw(sig);
}
}
catch (int &z)
{
cerr << "Caught exception: " << z << endl;
}
return 0;
}
int main()
{
try
{
test_signal();
}
catch (int &z)
{
cerr << "Caught unexpected exception: " << z << endl;
}
return 0;
}