Throwing an Exception from Within a Signal Handler

Throwing an exception from within a signal handler

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.

How to catch custom exception from signal handler in asyncio?

handle_sig is a callback, so it runs directly off the event loop and its exceptions are just reported to the user via a global hook. If you want the exception raised there to be caught elsewhere in the program, you need to use a future to transfer the exception from handle_sig to where you want it noticed.

To catch the exception at top-level, you probably want to introduce another method, let's call it async_main(), that waits for either self.do_io() or the previously-created future to complete:

    def __init__(self):
self.loop = asyncio.get_event_loop()
self.done_future = self.loop.create_future()

async def async_main(self):
# wait for do_io or done_future, whatever happens first
io_task = asyncio.create_task(self.do_io())
await asyncio.wait([self.done_future, io_task],
return_when=asyncio.FIRST_COMPLETED)
if self.done_future.done():
io_task.cancel()
await self.done_future # propagate the exception, if raised
else:
self.done_future.cancel()

To raise the exception from inside handle_sig, you just need to set the exception on the future object:

    def handle_sig(self, signum):
print(f"\npid: {os.getpid()}, Received signal: {Signals(signum).name}, raising error for exit")
self.done_future.set_exception(ShutdownApp("Exiting"))

Finally, you modify run_app to pass self.async_main() to run_until_complete, and you're all set:

$ python3 x.py
Starting Program
io start. Press Ctrl+C now.
^C
pid: 2069230, Received signal: SIGINT, raising error for exit
ShutdownApp caught: Exiting
Finished

In closing, note that reliably catching keyboard interrupts is a notoriously tricky undertaking and the above code might not cover all the corner cases.

Why doesn't my signal handler (which throws an exception) trigger more than once?

Signals and exceptions are not related to each other. What you're using (throwing exceptions from async signal handlers) is only portable between the few compilers that support it, such as GCC and Intel C/C++ with -fnon-call-exceptions.

That said, what you forgot to do is unblock the signal: when a signal handler is executing, the delivery of the same signal is blocked, and it does not become unblocked when the signal handler is exited through an exception. Change the signal handler as follows:

void SigactionHookHandler( int iSignal, siginfo_t * psSiginfo, void * psContext
{
cout << "Signal Handler Exception Caught: std::exception -- signal : " << iSignal << " from SigactionHookHandler()" << endl;

sigset_t x;
sigemptyset (&x);
sigaddset(&x, SIGSEGV);
sigprocmask(SIG_UNBLOCK, &x, NULL);

throw std::exception();
}

Exception not caught after signal

On most system the stack frame used by the signal handler is not a standard function stack frame as defined by the compiler for function calls.

So throwing out of a sig handler is not supported.

Stack frame for signal handling in the Linux Kernel

From the discussion in the linked question, on a linux system they are not even using the same stack for the stack frame and returning requires jumping back to a system function to restore the original user stack.

Unless the OS is specifically designed to handle exceptions then this is not going to work.

The rule of thumb for signal handlers is to do as little as possible in a signal handler. Set a global flag that can be detected by your normal code then check that flag periodically in your normal code to see when the signal has happened.

C++ exceptions and signal handlers

In my opinion that part doesn't really make much sense with current C++.

In C++ there is no way to use an exception as a signal because signals are meant to be executing an handler and then (possibly) continue execution.

C++ exceptions however don't work this way. Once you get to the exception handler the stack has been already rolled back and there is no way to "continue" after the handling: there is no way to get to the statement following a throw (or following a function call during which an exception gets thrown).

Signals are asynchronous but not disruptive and continuing after a signal is possible (even if of course care must be taken about what is done in the signal handler), exceptions instead are disruptive of the program flow and continuing is impossible.

I'd say the two ideas are incompatible at a logical level and doesn't really matter if the library is reentrant or not.

May be in early C++ design there was a resume option for exceptions...

Catch a C++ unhandled exception in the signal handler and resume application

Here is how I solved my problem (in pseudocode). There were probably other ways, I'm still all ears opened :

void malfunction()
{

terminate_handler previousTerminateHandler = set_terminate(callbackUnhandledExceptionTerminateHandler);

registerCallback(throwingCb);
launchTheTest();
waitForErrorEvent();
saveTheResult();
doSomeCleanUp();

set_terminate(previousTerminateHandler);

return;
}

static void callbackUnhandledExceptionTerminateHandler()
{
try{ throw; }
catch(const exception& e) {
cout << e.what() << endl;
notifyTheErrorEvent();
}catch(...){
cout << "callbackUnhandledExceptionTerminateHandler : else ???" << endl;
abort();
}

//Log whatever information, you need such as pid, stack or whatever
cout << "processId = " << getpid() << endl;

//Here if you return from this handler, it will call abort();
//You could let it ends, if you already logged what you needed.
//You could also trap the thread, if this is something you already do elsewhere before stopping the application.

}

In my case, I could check the service would be resilient to a crash of the customer application, and still be able to let other (or even the same!) open another proxy and do more work. Now left to do is to make sure no memory leak is caused here. (only to answer to whom would question why all this).

In a more general case, this same handling could be used to print the stack upon unhandled exception, without the need of gdb.

Why signal handler for SIGSEGV doesn't catch my C++ throw exception?

You can install a termination handler with std::set_terminate - the installed handler will then be invoked for unhandled exception. There is no strong guarantee, but there is a great chance that the invocation of abort that causes invocation of the termination handler is occurring on the top of the stack that threw the exception and thus you can still collect a backtrace.

Also, you can install a handler for SIGABRT, that will work as well:

#include <execinfo.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>

void handler(int sig, siginfo_t*, void*) {
void *array[10];
size_t size = backtrace(array, 10);
fprintf(stderr, "Error: signal %d:\n", sig);
backtrace_symbols_fd(array, size, STDERR_FILENO);
exit(1);
}

void terminate_handler()
{
}

int main()
{
struct sigaction new_sigaction, old_sigaction;
new_sigaction.sa_flags = SA_SIGINFO;
new_sigaction.sa_sigaction = handler;
sigaction(SIGABRT, &new_sigaction, &old_sigaction);
throw 1;
}

I'd personally prefer the termination handler, since in that case you explicitly know the cause. If the SIGABRT handler is invoked you must understand if this is because an unhandled exception invoked abort() or the signal was sent by other means.



Related Topics



Leave a reply



Submit