to C handler 2 Handler sets a flag to indicate interrupt happened 3 Handler requests interpreter to run PyErr_CheckSignals as soon as convenient 4 Main thread continues where it was interrupted Later, PyErr_CheckSignals runs the Python handler or raises KeyboardInterrupt. 2 / 5
Drop the GIL 2 Make the system call 3 Reacquire the GIL 4 If the system call was interrupted, call PyErr_CheckSignals If signal handler raised an exception, bail out Otherwise, go back to step 1 (PEP 0475). 3 / 5
Drop the GIL 2 Make the system call 3 Reacquire the GIL 4 If the system call was interrupted, call PyErr_CheckSignals If signal handler raised an exception, bail out Otherwise, go back to step 1 (PEP 0475). signal delivered here is okay 3 / 5
Drop the GIL 2 Make the system call 3 Reacquire the GIL 4 If the system call was interrupted, call PyErr_CheckSignals If signal handler raised an exception, bail out Otherwise, go back to step 1 (PEP 0475). signal delivered here is okay signal delivered here causes race 3 / 5
C handler 2 Handler sets a flag to indicate interrupt happened 3 Handler requests interpreter to run PyErr_CheckSignals as soon as convenient 4 Handler writes a byte to a file descriptor 5 Main thread continues where it was interrupted 4 / 5
a pipe, set write end as wakeup FD Eliminate all blocking calls, except select/poll Include the read end of the wakeup FD in the call Remember to consume the byte written Good News Everyone: tornado and asyncio do this 5 / 5
a pipe, set write end as wakeup FD Eliminate all blocking calls, except select/poll Include the read end of the wakeup FD in the call Remember to consume the byte written Good News Everyone: tornado and asyncio do this 5 / 5