tags:

views:

229

answers:

3
#include <iostream>
#include <signal.h>
#include <fenv.h>
#include <string.h>

void signal_handler(int sig, siginfo_t *siginfo, void* context) 
{ 
  std::cout << " signal_handler " << fetestexcept(FE_ALL_EXCEPT) << std::endl;
  throw "exception"; 
}

void divide() {
  float a = 1000., b = 0.,  c, f = 1e-300;
  c = a / b;

  std::cout << c << " and f = " << f << std::endl;  
}

void init_sig_hanlder() {
  feenableexcept(FE_ALL_EXCEPT);

  struct sigaction sa, initial_sa;

  sa.sa_sigaction   = &signal_handler ;
  sigemptyset( &sa.sa_mask ) ;
  sa.sa_flags   = SA_SIGINFO;   // man sigaction(3) // allows for void(*)(int,siginfo_t*,void*) handler

  sigaction(SIGFPE, &sa, &initial_sa);

}

int main(int argc, char** argv) {
  init_sig_hanlder();

  while(true)
    {
      try {
    sleep(1);
    divide();
      }
      catch(const char * a) {
    std::cout << "Exception in catch: " << a << std::endl;
      }    
      catch(...) {
    std::cout << "Exception in ..." << std::endl;
      }    
    }

  return 0;
}

Produce the following results on Linux/g++4.2:

signal_handler 0
Exception in catch: exception
inf and f = 0
inf and f = 0
inf and f = 0
inf and f = 0

So, signal handler is executed the first time but the next fp exception does not trigger the handler again. Where am I wrong ?

+2  A: 

As I recall signal handlers have to be declared 'extern "C"' because the library / kernel is going to use C calling conventions, not the C++ calling convention, for your function. But functions that are extern "C" cannot throw exceptions, so at least formally your code is not "right"

Under the hood, I would guess the signal delivery code in Linux does not have a chance to reset or clear the signal masks because you never returned control to the runtime and the kernel.

coryan
//Copied from answer to Tomer VromenOk, in fact my code is more complex than this. I will check if the handler returns or throw an exception because it seems to be a good explanation.
Henry Fané
A closer look at the code showed that I do not throw an exception in the handler. The real problem is to go to the next instruction. On solaris is was done with the following code:<br> aContext->uc_mcontext.gregs[REG_PC] = aContext->uc_mcontext.gregs[REG_nPC];
Henry Fané
I cannot find a similar example on Linux platform, I got EIP register pointing to the next instruction but I dont know how to push the value.
Henry Fané
+2  A: 

I don't think throwing an exception inside a signal handler is good practice. The operating system expects the signal handler to return, because the signal is blocked while the handler for it is called. By throwing an exception you prevent the system from unblocking the signal.

Tomer Vromen
Ok, in fact my code is more complex than this. I will check if the handler returns or throw an exception because it seems to be a good explanation.
Henry Fané
as well as throw from a handler not being allowed, using cout is not a good idea. It is very likely not signal safe. In this simple program it may not matter, but if you are going to do IO in a handler, open/write/close is probably the only safe way.
Peeter Joot
A: 

You should be using sigsetjmp/siglongjmp for code like this, not exceptions.

Using exceptions here is wrong, and the jmp change should be the first step to rule out odd platform specific behaviour (especially since the C++ standard allows for exceptions to be implemented using signal delivery mechanisms).

However, I'm curious if the failure for the SIGFPE to be raised has something to do with the fe*except() state.

Question: What happens to this state before and after doing your initial divide by zero? Perhaps there is an expectation that feclearexcept() is required to get another SIGFPE when you try this again.

Peeter Joot
In fact, I am doing the following:- create a C++ object on the stack, which will be tested after computational code,- call computation code which causes a SIGFPE,- signal handler is called and set some flags in the previously created object,- the Check() method is called and can raises exceptionsThe first mistake was to launch an exception in the handler, so I suppressed this call but the program return to the ofending operation. I would like to skip this instruction but resetting FPU flags with feclearexcept()/fesetenv() does not work. I will take a look at sigsetjmp/siglongjmp.
Henry Fané