I have a problem trying to abort a Python script, for example:
#!/usr/bin/python
import hello
hello.draw()
exit()
where draw() is a C function. In this function there is a loop to print some text. I tried CTRL-C to break out of it but it doesn't work. Important thing is that C code cannot be modified!
Any ideas?
I put together an example of how you might do this. It lets to augment the function and escape from it without needing to modify it. For example, given the function foo
(which was inline in test.h for my testing):
void foo() {
while(1); // never returns
}
You can play a game with signals and siglongjmp
. It should be noted however that you need to be very careful to ensure you only call async safe functions from within foo
, or alternatively mask the signals before and unmask them after any unsafe calls.
We can then wrap the function and use %exception
to inject some extra code to allow the signal to backout of the function:
%module test
%{
#include <setjmp.h>
#include <signal.h>
static sigjmp_buf timeout;
static void backout(int sig) {
siglongjmp(timeout, sig);
}
#include "test.h"
%}
%include <exception.i>
%exception {
if (!sigsetjmp(timeout, 1)) {
signal(SIGALRM,backout); // Check return?
$action
}
else {
// raise a Python exception
SWIG_exception(SWIG_RuntimeError, "Timeout in $decl");
}
}
void foo();
%exception;
Which allows us to write:
import signal
import test
signal.alarm(5)
test.foo()
Which works as hoped, given the caveats about async-safe functions. I used SIGALRM
here because it allowed me to keep it all self-contained within one Python file. You could use another thread/process and other signals if you preferred.
If you change SIGALRM in my example to SIGINT (and don't raise a SIGALRM either obviously) it'll work with Ctrl+C - the reason it doesn't is because of how the default Python signal handlers work. The default handler Python uses simply sets a flag and processes the signal after the underlying call has returned control to Python, which neatly side-steps the whole async-safety issue.