[issue35935] threading.Event().wait() not interruptable with Ctrl-C on Windows

Eryk Sun report at bugs.python.org
Wed Mar 3 12:50:09 EST 2021


Eryk Sun <eryksun at gmail.com> added the comment:

Alexander, I wrote the above sample function to be slotted directly into the existing design based on the SIGINT event. I wasn't looking to rewrite everything using user-mode APCs and alertable waits. A change like that could have ramifications for applications that already use alertable waits, depending on how resiliently they're designed.

> Originally, Windows devised an APC mechanism to simulate asynchronous
> delivery of Posix signal to threads. 

IIRC, Iterix (i.e. SUA -- replaced nowadays by WSL) used Asynchronous Procedure Calls (APCs) to implement Unix signals. But APCs certainly weren't devised solely for the purpose of the providing signals in the POSIX subsystem. They're an evolution of Asynchronous System Traps (ASTs) in DEC VMS. (The lead designer of NT and most of the development team originally designed and implemented VMS at DEC. They began working at Microsoft to design the NT system and OS/2, Win32, and POSIX subsystems starting in late 1988.) Kernel-mode and user-mode APCs are fundamental and critical to NT (e.g. thread termination uses an APC), and particularly the I/O system, which uses a special kernel-mode APC for I/O request completion. (An I/O request is often serviced in an arbitrary thread context. Buffered I/O completion has to be queued back to the calling thread in order to copy from a system buffer to the user buffer.)

> Those APCs are invoked during alertable wait functions. Delivery 
> of an APS also aborts the wait with WAIT_IO_COMPLETION return code.

WAIT_IO_COMPLETION is the same as STATUS_USER_APC, because I/O completion routines are queued as user-mode APCs (e.g. by ReadFileEx). Using the name "WAIT_IO_COMPLETION" clarifies the intent in this case. In general, I prefer "STATUS_USER_APC".

> An APC queue can be processed at any time by calling an alertable 
> wait function with zero timeout, for example SleepEx(0, TRUE).

The user-mode APC queue can also be pumped by calling the NtTestAlert() system function. For example:

    import ctypes
    ntdll = ctypes.WinDLL('ntdll')
    kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)

    @ctypes.WINFUNCTYPE(None, ctypes.c_void_p)
    def apc(p):
        print('spam APC')

    hthread = ctypes.c_void_p(-2)
    kernel32.QueueUserAPC(apc, hthread, None)


    >>> ntdll.NtTestAlert(hthread)
    spam APC
    0

----------

_______________________________________
Python tracker <report at bugs.python.org>
<https://bugs.python.org/issue35935>
_______________________________________


More information about the Python-bugs-list mailing list