Author eryksun
Recipients Chris Billington, eryksun, paul.moore, steve.dower, tim.golden, zach.ware
Date 2021-03-03.03:43:05
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <>
> But on this particular issue, making the unconditional wait be 
> interruptable by signals shouldn't be impossible.

PyThread_acquire_lock_timed() in Python/thread_nt.h currently ignores intr_flag. The current implementation calls EnterNonRecursiveMutex(), which in turn calls PyCOND_WAIT() / PyCOND_TIMEDWAIT() in Python/condvar.h. EnterNonRecursiveMutex() needs to support intr_flag and support a return value that indicates the wait was interrupted. PyThread_acquire_lock_timed() needs to handle this value by returning PY_LOCK_INTR.

When using emulated condition variables, a lock combines a semaphore and a critical section. Waiting on the semaphore can be integrated with the SIGINT event via WaitForMultipleObjects(). 

Here's a replacement for WaitForSingleObject() that integrates the SIGINT event, and supports long waits passed as a PY_TIMEOUT_T in microseconds (just for the sake of discussion; it's not rigorously tested code):

    unsigned long
    _Py_WaitForSingleObject(void *handle, PY_TIMEOUT_T microseconds,
                            int intr_flag)
        DWORD result;
        DWORD handle_count;
        HANDLE handle_array[2];
        HANDLE sigint_event = NULL;

        LONGLONG timeout = -1;
        ULONGLONG deadline = 0;

        /* Store timeout in system time units of 100 ns. */
        if (microseconds >= 0) {
            timeout = microseconds * 10;
            deadline += timeout;

        handle_count = 1;
        handle_array[0] = (HANDLE)handle;

        if (intr_flag) {
            sigint_event = _PyOS_SigintEvent();
            if (sigint_event) {
                handle_array[handle_count++] = sigint_event;

        do {
            ULONGLONG now;
            DWORD milliseconds;

            if (timeout < 0) {
                milliseconds = INFINITE;
            } else if (timeout < INFINITE * 10000) {
                milliseconds = timeout / 10000;
            } else {
                milliseconds = INFINITE - 1;

            result = WaitForMultipleObjectsEx(
                        handle_count, handle_array, FALSE,
                        milliseconds, FALSE);

            if (sigint_event && result == WAIT_OBJECT_0 + 1) {
                /* Pretend that this was an alertable wait that
                   was interrupted by a user-mode APC queued to
                   the main thread by the C signal handler. It's
                   not implemented that way, but it could be. */
                result = STATUS_USER_APC;

            if (result != WAIT_TIMEOUT) {

            timeout = deadline - now;
        } while (timeout >= 0);

        return result;

If the wait returns STATUS_USER_APC, then the caller should call PyErr_CheckSignals(). intr_flag would presumably only be true when called from a thread that can handle signals, i.e. when _PyOS_IsMainThread() is true.

That said, if actual Windows condition variables are used (an alternate implementation in Python/condvar.h), then waiting is implemented via SleepConditionVariableSRW(). There's no way to integrate the SIGINT event with this wait, nor any documented way to cancel the wait from the console control thread. If this implementation is adopted, then maybe the few cases that require locks that support an interruptible wait can be implemented as a separate thread API.
Date User Action Args
2021-03-03 03:43:07eryksunsetrecipients: + eryksun, paul.moore, tim.golden, zach.ware, steve.dower, Chris Billington
2021-03-03 03:43:07eryksunsetmessageid: <>
2021-03-03 03:43:06eryksunlinkissue35935 messages
2021-03-03 03:43:05eryksuncreate