Skip to content

Reinstall signal handler when Python signal handler called #228

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion src/cysignals/implementation.c
Original file line number Diff line number Diff line change
Expand Up @@ -681,7 +681,14 @@ static void setup_cysignals_handlers(void)
sigprocmask(SIG_SETMASK, &default_sigmask, &sigmask_with_sigint);
#endif

/* Install signal handlers */
/* Install signal handlers. We need a separate C-level interrupt handler
* apart from the Python-level interrupt handler because Python-level
* interrupt handler will not be called inside Cython code.
* See init_cysignals and python_check_interrupt for an explanation.
* A downside is the C-level interrupt handler will be overwritten
* when signal(SIGINT, getsignal(SIGINT)) is executed, in that case
* init_cysignals() need to be called again.
*/
/* Handlers for interrupt-like signals */
sa.sa_handler = cysigs_interrupt_handler;
sa.sa_flags = 0;
Expand Down
1 change: 1 addition & 0 deletions src/cysignals/signals.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ cdef extern from "struct_signals.h":

ctypedef struct cysigs_t:
cy_atomic_int sig_on_count
cy_atomic_int interrupt_received
cy_atomic_int block_sigint
const char* s
PyObject* exc_value
Expand Down
6 changes: 6 additions & 0 deletions src/cysignals/signals.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,12 @@ def python_check_interrupt(sig, frame):
``implementation.c``.
"""
sig_check()
# If this line is reached, a possibility is that something called
# signal(SIGINT, getsignal(SIGINT)), which cause cysigs_interrupt_handler
# to not be called. We reinstall the C-level signal handler.
init_cysignals()
cysigs.interrupt_received = sig
sig_check()


cdef void verify_exc_value() noexcept:
Expand Down
10 changes: 9 additions & 1 deletion src/cysignals/tests.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ from libc.errno cimport errno
from posix.signal cimport sigaltstack, stack_t, SS_ONSTACK

from cpython cimport PyErr_SetString
from cpython.exc cimport PyErr_CheckSignals

from .signals cimport *
from .memory cimport *
Expand Down Expand Up @@ -805,6 +806,13 @@ def test_interrupt_bomb(long n=100, long p=10):
i = 0
while True:
try:
# For some reason, without the line below, the exception
# will be detected too late (outside the try/except block)
# and the KeyboardInterrupt will be leaked outside,
# making the test fail.
# We can't really call PyErr_CheckSignals() from inside
# sig_on() because it does not hold the GIL.
PyErr_CheckSignals()
with nogil:
sig_on()
ms_sleep(1000)
Expand Down Expand Up @@ -1124,7 +1132,7 @@ def test_sig_block_outside_sig_on(long delay=DEFAULT_DELAY):
sig_unblock()

try:
sig_on() # Interrupt caught here
PyErr_CheckSignals() # Interrupt caught here
except KeyboardInterrupt:
return "Success"
abort() # This should not be reached
Expand Down
Loading