views:

105

answers:

2

I try to inject the exception to thread using signals, but some times the exception is not get caught. For example the following code:

void _sigthrow(int sig)
{
    throw runtime_error(strsignal(sig));
}
struct sigaction sigthrow = {{&_sigthrow}};

void* thread1(void*)
{
    sigaction(SIGINT,&sigthrow,NULL);
    try
    {
        while(1) usleep(1);
    }
    catch(exception &e)
    {
        cerr << "Thread1 catched " << e.what() << endl;
    }
};

void* thread2(void*)
{
    sigaction(SIGINT,&sigthrow,NULL);
    try
    {
        while(1);
    }
    catch(exception &e)
    {
        cerr << "Thread2 catched " << e.what() << endl; //never goes here
    }
};

If I try to execute like:

int main()
{
    pthread_t p1,p2;

    pthread_create( &p1, NULL, &thread1, NULL );
    pthread_create( &p2, NULL, &thread2, NULL );

    sleep(1);

    pthread_kill( p1, SIGINT);
    pthread_kill( p2, SIGINT);

    sleep(1);

    return EXIT_SUCCESS;
}

I get the following output:

Thread1 catched Interrupt
terminate called after throwing an instance of 'std::runtime_error'
  what():  Interrupt
Aborted

How can I make second threat catch exception? Is there better idea about injecting exceptions?

A: 

G++ assumes that exceptions can only be thrown from function calls. If you're going to violate this assumption (eg, by throwing them from signal handlers), you need to pass -fnon-call-exceptions to G++ when building your program.

Note, however that this causes G++ to:

 Generate code that allows trapping instructions to throw
 exceptions.  Note that this requires platform-specific runtime
 support that does not exist everywhere.  Moreover, it only allows
 _trapping_ instructions to throw exceptions, i.e. memory
 references or floating point instructions.  It does not allow
 exceptions to be thrown from arbitrary signal handlers such as
 `SIGALRM'.

This means that exceptioning out from the middle of some random code is NEVER safe. You can only except out of SIGSEGV, SIGBUS, and SIGFPE, and only if you pass -fnon-call-exceptions and they were triggered due to a fault in the running code. The only reason this worked on thread 1 is because, due to the existence of the usleep() call, G++ was forced to assume that it might throw. With thread 2, G++ can see that no trapping instruction was present, and eliminate the try-catch block.

You may find the pthread cancellation support more akin to what you need, or otherwise just add a test like this somewhere:

if (*(volatile int *)terminate_flag) throw terminate_exception();
bdonlan
Is it possible to make sure that g++ will think that some function may throw exception? Calling virtual method with infinitive loop wont make that trick.
lionbest
I even call some virtual function from try, and then `while(1) if (*(volatile int *)terminate_flag) throw terminate_exception(); else do_infinite_loop();` to trick it that exception may occur, but it wont.When I add -O2 flag and it magicly start works! It wont with -O1. I wonder what -O2 change that make it work;
lionbest
In general, this is not going to be safe. If something works on one optimization level and not on another, it means by sheer chance the planets aligned to make things work. Modify some unrelated code, upgrade/downgrade your compiler, or sneeze on it wrong, and it might break again. _Don't rely on it_. After all, even if it's ready to get an exception from your virtual function, the exception might end up coming from the loop code...
bdonlan
+1  A: 

In Boost.thread a thread can be interrupted by invoking the interrupt() member function of the corresponding boost::thread object. It uses pthread condition variables to communicate with the thread and allows you to define interruption points in the thread code. I would avoid use of pthread_kill in C++. The fact that boost thread doesn't use pthread_kill anywhere in their code confirms this I think.

Eddy Pronk
From boost doc `When the interrupted thread next executes one of the specified interruption points`. So calling `thread::interrupt()` will never exec from while(1); loop.
lionbest
@lionbest : Yes, this is why you need to define interruption points, but this enables you to end you thread in a very controlled manner. Using a flag the way @bdonlan suggested would give you the same level of control, but try to get rid of the pthread_kill if you can.
Eddy Pronk
@Eddy Pronk:You are right, defining interruption points is the right solution. Somehow I'm testing where can I use pthread_kill, but it seams to be vary.
lionbest