Allow Ctrl-C to interrupt python C-extension

I am running some computationally heavy simulations in (self-made) Python extensions. Sometimes I am mistaken and would like to stop the simulation. However, Ctrl-C does not seem to have any effect (other than printing ^C on the screen, so I have to end the process using kill or the system monitor.

As far as I can see, python just waits for the C extension to complete and does not communicate with it during this time.

Is there any way to make this work?

Update: The basic answer (for my particular problem) were: 1. rewrite the code to transmit regularly to the control back to the calling party (answer , allowing Ctrl-C to interrupt the expansion of C python below), or 2. Use PyErr_CheckSignals() (answer qaru.site / questions / 790571 / ... below)

+9
source share
4 answers

I would review C extensions so that they do not run for a long period of time.

So, divide them into more elementary steps (each of which takes a short period of time, for example, from 10 to 50 milliseconds), and these more elementary steps are called using Python code.

the style of continuing the passage can be important for understanding how the style of programming ...

+1
source

Python has a signal handler installed on SIGINT that simply sets a flag that is checked by the outline of the main interpreter. For this handler to work correctly, the Python interpreter must run Python code.

You have several options available to you:

  • Use Py_BEGIN_ALLOW_THREADS / Py_END_ALLOW_THREADS to release a GIL around your C extension code. You cannot use any Python functions unless you hold a GIL, but Python code (and other C code) can execute at the same time as your C thread (true multithreading). A separate Python thread can run along with the C extension and trigger Ctrl + C signals.
  • Set up your own SIGINT handler and call the original signal handler (Python). Then your SIGINT handler can do whatever it needs to cancel the C extension code and return control to the Python interpreter.
+6
source

However, Ctrl-C doesn't seem to have an effect.

Ctrl-C in the shell sends SIGINT to the foreground process group . python sets a flag in C code when it receives a signal. If the C extension runs in the main thread, then no Python signal handler will be launched (and therefore you will not see a KeyboardInterrupt exception on Ctrl-C ) unless you call PyErr_CheckSignals() , which checks the flag (this means: it should not slow you down ) and, if necessary, starts Python signal handlers, or if your simulation allows you to execute Python code (for example, if the simulation uses Python callbacks). If the extension works in the background thread, then just release the GIL (to allow Python code to run in the main thread, which allows signal handlers to run).

Related: Logging into Cython, Python and KeybordInterrupt

+5
source

There is an alternative way to solve this problem, if you do not want your C extension (or ctypes DLL) to be bound to Python, for example, in the case when you want to create a C library with bindings in several languages, you must enable the C Extension to run for extended periods, and you can change the extension C:

Include the signal header in extension C.

 #include <signal.h> 

Create a typedef signal handler in extension C.

 typedef void (*sighandler_t)(int); 

Add signal handlers to the C extension, which will perform the actions necessary to interrupt any long-running code (set a stop flag, etc.) and save existing Python signal handlers.

 sighandler_t old_sig_int_handler = signal(SIGINT, your_sig_handler); sighandler_t old_sig_term_handler = signal(SIGTERM, your_sig_handler); 

Restore existing signal handlers every time the extension C. returns. This step ensures that Python signal handlers are reapplied.

 signal(SIGINT, old_sig_int_handler); signal(SIGTERM, old_sig_term_handler); 

If the long code is interrupted (flag, etc.), return a Python control with a return code indicating the signal number.

 return SIGINT; 

In Python, send the signal received in extension C.

 import os import signal status = c_extension.run() if status in [signal.SIGINT, signal.SIGTERM]: os.kill(os.getpid(), status) 

Python will do what you expect, such as raising KeyboardInterrupt for SIGINT.

+2
source

All Articles