# 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.