Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I catch an Interrupt signal in Python when inside a blocking boost c++ method?

Tags:

c++

python

boost

I have a toolset which has been written in C++, and given Boost bindings for Python.

Initially, this code was all C++, and I caught a CTRL+C interrupt with:

signal( SIGINT, signalCallbackHandler );

and

void signalCallbackHandler(int /*signum*/)
{
    g_myObject->stop();
}

This worked fine.

However, now I've added the Python bindings in, I'm using Python to initialise the objects.

My initial thought was to do it like this:

import signal

def handle_interrupt( signum, frame ) :
    g_myObject.stop()

signal.signal( signal.SIGINT, handle_interrupt )
g_myObject = MyObject()
g_myObject.start()

However, this signal handler is never called.

How should I be handling an interrupt like this? Do I need to do it inside C++, and then call the Python function from there?

like image 672
Hugh Avatar asked Sep 04 '26 20:09

Hugh


1 Answers

Your python signal handler is not called because python defers execution of signal handlers until after the next bytecode instruction is to be executed - see the library documentation for signal, section 18.8.1.1:

A Python signal handler does not get executed inside the low-level (C) signal handler. Instead, the low-level signal handler sets a flag which tells the virtual machine to execute the corresponding Python signal handler at a later point(for example at the next bytecode instruction). This has consequences:

  • It makes little sense to catch synchronous errors like SIGFPE or SIGSEGV that are caused by an invalid operation in C code. Python will return from the signal handler to the C code, which is likely to raise the same signal again, causing Python to apparently hang. From Python 3.3 onwards, you can use the faulthandler module to report on synchronous errors.
  • A long-running calculation implemented purely in C (such as regular expression matching on a large body of text) may run uninterrupted for an arbitrary amount of time, regardless of any signals received. The Python signal handlers will be called when the calculation finishes.

The reason for this is that a signal can arrive at any time, potentially half way through the execution of a python instruction. It would not be safe for the VM to begin executing the signal handler, because the VM is in an unknown state. Therefore, the actual signal handler installed by python merely sets a flag telling the VM to call the signal handler after the current instruction is complete.

If the signal arrives during execution of your C++ function, then the signal handler sets the flag and returns back to your C++ function.

If the main purpose of the signal handler is to allow the C++ function to be interrupted, then I suggest you dispense with the Python signal handler and install a C++ signal handler that sets a flag which triggers an early exit in your C++ code (presumably returning a value that indicates it was interrupted).

That approach would allow you to use the same code regardless of whether you are calling your code from python, C++ or perhaps another binding.

like image 117
harmic Avatar answered Sep 06 '26 11:09

harmic