I live in an environment with Win7/MSVC 2010sp1, two different Linux boxes (Red Hat) with g++ versions (4.4.7, 4.1.2), and AIX with xlc++ (08.00.0000.0025).
Not so long ago it was requested that we move some code from AIX over to Linux. It didn't take too long to see that Linux was a bit different. Normally when a signal is thrown, we handle it and throw a C++ exception. That was not working as expected.
Long story short, throwing c++ exceptions from a signal handler isn't going to work.
Sometime later, I put together a fix that uses setjmp/longjmp to move the exception out of the handler. Aftersome testing and the dang thing works on all platforms. After an obligatory round of cubical happy dance I moved on to setting up some unit tests. Oops.
Some of my tests were failing on Linux. What I observed was that the raise function only worked once. With two tests using SIGILL, the first one passed, and the second one failed. I broke out an axe, and started chopping away at the code to remove as much cruft as possible. That yielded this smaller example.
#include <csetjmp>
#include <iostream>
#include <signal.h>
jmp_buf mJmpBuf;
jmp_buf *mpJmpBuf = &mJmpBuf;
int status = 0;
int testCount = 3;
void handler(int signalNumber)
{
signal(signalNumber, handler);
longjmp(*mpJmpBuf, signalNumber);
}
int main(void)
{
if (signal(SIGILL, handler) != SIG_ERR)
{
for (int test = 1; test <= testCount; test++)
{
try
{
std::cerr << "Test " << test << "\n";
if ((status = setjmp(*mpJmpBuf)) == 0)
{
std::cerr << " About to raise SIGILL" << "\n";
int returnStatus = raise(SIGILL);
std::cerr << " Raise returned value " << returnStatus
<< "\n";
}
else
{
std::cerr << " Caught signal. Converting signal "
<< status << " to exception" << "\n";
std::exception e;
throw e;
}
std::cerr << " SIGILL should have been thrown **********\n";
}
catch (std::exception &)
{ std::cerr << " Caught exception as expected\n"; }
}
}
else
{ std::cerr << "The signal handler wasn't registered\n"; }
return 0;
}
For the Windows and the AIX boxes I get the expected output.
Test 1
About to raise SIGILL
Caught signal. Converting signal 4 to exception
Caught exception as expected Test 2
About to raise SIGILL
Caught signal. Converting signal 4 to exception
Caught exception as expected Test 3
About to raise SIGILL
Caught signal. Converting signal 4 to exception
Caught exception as expected
For both Linux boxes it looks like this.
Test 1
About to raise SIGILL
Caught signal. Converting signal 4 to exception
Caught exception as expected
Test 2
About to raise SIGILL
Raise returned value 0
SIGILL should have been thrown **********
Test 3
About to raise SIGILL
Raise returned value 0
SIGILL should have been thrown **********
So, my real question is "What is going on here?"
My retorical questions are:
- Is anyone else observing this behavior?
- What should I do to try to troubleshoot this issue?
- What other things should I be aware of?