Further topics in interrupt/signal handling¶
Testing interrupts¶
When writing documentation, one sometimes wants to check that certain
code can be interrupted in a clean way. The best way to do this is to
use cysignals.alarm()
.
The following is an example of a doctest demonstrating that the
SageMath function factor()
can be interrupted:
>>> from cysignals.alarm import alarm, AlarmInterrupt
>>> try:
... alarm(0.5)
... factor(10**1000 + 3)
... except AlarmInterrupt:
... print("alarm!")
alarm!
If you use the SageMath doctesting framework, you can instead doctest
the exception in the usual way (the Python doctest
module exits
whenever a KeyboardInterrupt
is raised in a doctest).
To avoid race conditions, make sure that the calls to alarm()
and
the function you want to test are in the same doctest:
>>> alarm(0.5); factor(10**1000 + 3)
Traceback (most recent call last):
...
AlarmInterrupt
Signal handling without exceptions¶
There are several more specialized functions for dealing with interrupts. As
mentioned above, sig_on()
makes no attempt to clean anything up (restore
state or freeing memory) when an interrupt occurs. In fact, it would be
impossible for sig_on()
to do that. If you want to add some cleanup code,
use sig_on_no_except()
for this. This function behaves exactly like
sig_on()
, except that any exception raised (like KeyboardInterrupt
or
RuntimeError
) is not yet passed to Python. Essentially, the exception is
there, but we prevent Cython from looking for the exception. Then
cython_check_exception()
can be used to make Cython look for the exception.
Normally, sig_on_no_except()
returns 1. If a signal was caught and an
exception raised, sig_on_no_except()
instead returns 0. The following
example shows how to use sig_on_no_except()
:
def no_except_example():
if not sig_on_no_except():
# (clean up messed up internal state)
# Make Cython realize that there is an exception.
# It will look like the exception was actually raised
# by cython_check_exception().
cython_check_exception()
# (some long computation, messing up internal state of objects)
sig_off()
There is also a function sig_str_no_except(s)
which is analogous to
sig_str(s)
.
Note
See the file src/cysignals/tests.pyx
for more examples of how to use the various sig_*()
functions.
Releasing the Global Interpreter Lock (GIL)¶
All the functions related to interrupt and signal handling do not require the
Python GIL
(if you don’t know what this means, you can safely ignore this section), they
are declared nogil
. This means that they can be used in Cython code inside
with nogil
blocks. If sig_on()
needs to raise an exception, the GIL is
temporarily acquired internally.
If you use C libraries without the GIL and you want to raise an exception before calling sig_error(), remember to acquire the GIL while raising the exception. Within Cython, you can use a with gil context.
Warning
The GIL should never be released or acquired inside a sig_on()
block. If
you want to use a with nogil
block, put both sig_on()
and
sig_off()
inside that block. When in doubt, choose to use
sig_check()
instead, which is always safe to use.