[Numpy-discussion] Handling interrupts in NumPy extensions

David Cournapeau david at ar.media.kyoto-u.ac.jp
Wed Aug 23 23:11:34 EDT 2006


David M. Cooke wrote:
> On Wed, 23 Aug 2006 11:45:29 -0700
> Travis Oliphant <oliphant.travis at ieee.org> wrote:
>
>> I'm working on some macros that will allow extensions to be 
>> "interruptable" (i.e. with Ctrl-C).  The idea came from SAGE but the 
>> implementation is complicated by the possibility of threads and making 
>> sure to handle clean-up code correctly when the interrupt returns.
>>
>
This is funny, I was just thinking about that yesterday. This is a major 
problem when writing C extensions in matlab (the manual says use the 
matlab allocator instead of malloc/new/whatever, but when you call a 
library, you cannot do that...).
>
> Best way I can see this is to have a SIGINT handler installed that sets a
> global variable, and check that every so often. It's such a good way that
> Python already does this -- Parser/intrcheck.c sets the handler, and you can
> use PyOS_InterruptOccurred() to check if one happened. So something like
This is the way I do it when writing extension under matlab. I am by no 
means knowledgeable about those kind of things, but this is the simplest 
solution I came up with so far. I would guess that because it uses one 
global variable, it should not matter which thread receives the signal ?

>
> while (long running loop) {
>    if (PyOS_InterruptOccurred()) goto error:
>    ... useful stuff ...
> }
> error:
>
> This could be abstracted to a set of macros (with Perry's syntax):
>
> NPY_SIG_INTERRUPTABLE
>   while (long loop) {
>      NPY_CHECK_SIGINT;
>      .. more stuff ..
>   }
> NPY_SIG_END_INTERRUPTABLE
>
> where NPY_CHECK_SIGINT would do a longjmp().
Is there really a need for a longjmp ? What I simply do in this case is 
checking the global variable,
and if its value changes, goto to the normal error handling. Let's say 
you have already a good error handling in your function, as Travis 
described in his email:

status    = do_stuff();
    if (status < 0) {
        goto cleanup;
    }

Then, to handle sigint, you need a global variable got_sigint which is 
modified by the signal
handler, and check its value (the exact type of this variable is 
platform specific; on linux, I am using volatile sig_atomic_t, as 
recommeded by the GNU C doc)::

    /* status is 0 if everything is OK */
    status    = do_stuff();
    if (status < 0) {
        goto cleanup;
    }
    sigprocmask (SIG_BLOCK, &block_sigint, NULL);
    if (got_sigint) {
        got_sigint    = 0;
        goto cleanup;
    }
    sigprocmask (SIG_UNBLOCK, &block_sigint, NULL);

So the error handling does not be modified, and no longjmp is needed ? 
Or maybe I don't understand what you mean.

I think the case proposer by Perry is too restrictive: it is really 
common to use external libraries which we do not know whether they use 
memory allocation inside the processing, and there is a need to clean 
that too.
>
> Or come up with a good (fast) way to run stuff in another process :-)
>
This sounds a bit overkill, and a pain to implement for different 
platforms ? The checking of signals should be fast, but it has a cost 
(you have to use a branch) which prevents is from being called to often 
inside a loop, for example.

David





More information about the NumPy-Discussion mailing list