1Further topics in interrupt/signal handling
2===========================================
3
4Testing interrupts
5------------------
6
7When writing documentation, one sometimes wants to check that certain
8code can be interrupted in a clean way. The best way to do this is to
9use :func:`cysignals.alarm`.
10
11The following is an example of a doctest demonstrating that the
12SageMath function :func:`factor()` can be interrupted:
13
14.. code-block:: pycon
15
16    >>> from cysignals.alarm import alarm, AlarmInterrupt
17    >>> try:
18    ...     alarm(0.5)
19    ...     factor(10**1000 + 3)
20    ... except AlarmInterrupt:
21    ...     print("alarm!")
22    alarm!
23
24If you use the SageMath doctesting framework, you can instead doctest
25the exception in the usual way (the Python ``doctest`` module exits
26whenever a ``KeyboardInterrupt`` is raised in a doctest).
27To avoid race conditions, make sure that the calls to ``alarm()`` and
28the function you want to test are in the same doctest:
29
30.. code-block:: pycon
31
32    >>> alarm(0.5); factor(10**1000 + 3)
33    Traceback (most recent call last):
34    ...
35    AlarmInterrupt
36
37.. _advanced-sig:
38
39Signal handling without exceptions
40----------------------------------
41
42There are several more specialized functions for dealing with interrupts. As
43mentioned above, ``sig_on()`` makes no attempt to clean anything up (restore
44state or freeing memory) when an interrupt occurs. In fact, it would be
45impossible for ``sig_on()`` to do that. If you want to add some cleanup code,
46use ``sig_on_no_except()`` for this. This function behaves *exactly* like
47``sig_on()``, except that any exception raised (like ``KeyboardInterrupt`` or
48``RuntimeError``) is not yet passed to Python. Essentially, the exception is
49there, but we prevent Cython from looking for the exception. Then
50``cython_check_exception()`` can be used to make Cython look for the exception.
51
52Normally, ``sig_on_no_except()`` returns 1. If a signal was caught and an
53exception raised, ``sig_on_no_except()`` instead returns 0. The following
54example shows how to use ``sig_on_no_except()``::
55
56    def no_except_example():
57        if not sig_on_no_except():
58            # (clean up messed up internal state)
59
60            # Make Cython realize that there is an exception.
61            # It will look like the exception was actually raised
62            # by cython_check_exception().
63            cython_check_exception()
64        # (some long computation, messing up internal state of objects)
65        sig_off()
66
67There is also a function ``sig_str_no_except(s)`` which is analogous to
68``sig_str(s)``.
69
70.. NOTE::
71
72    See the file `src/cysignals/tests.pyx <https://github.com/sagemath/cysignals/blob/master/src/cysignals/tests.pyx>`_
73    for more examples of how to use the various ``sig_*()`` functions.
74
75Releasing the Global Interpreter Lock (GIL)
76-------------------------------------------
77
78All the functions related to interrupt and signal handling do not require the
79`Python GIL
80<http://docs.cython.org/src/userguide/external_C_code.html#acquiring-and-releasing-the-gil>`_
81(if you don't know what this means, you can safely ignore this section), they
82are declared ``nogil``. This means that they can be used in Cython code inside
83``with nogil`` blocks. If ``sig_on()`` needs to raise an exception, the GIL is
84temporarily acquired internally.
85
86If you use C libraries without the GIL and you want to raise an exception before
87calling :ref:`sig_error() <sig-error>`, remember to acquire the GIL while
88raising the exception. Within Cython, you can use a `with gil context
89<http://docs.cython.org/src/userguide/external_C_code.html#acquiring-the-gil>`_.
90
91.. WARNING::
92
93    The GIL should never be released or acquired inside a ``sig_on()`` block. If
94    you want to use a ``with nogil`` block, put both ``sig_on()`` and
95    ``sig_off()`` inside that block. When in doubt, choose to use
96    ``sig_check()`` instead, which is always safe to use.
97
98