gh-125541: Make Ctrl-C interrupt threading.Lock.acquire() on Windows (#125546)

This commit is contained in:
Sam Gross 2024-10-17 14:10:55 -04:00 committed by GitHub
parent b454662921
commit d8c8648161
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 25 additions and 8 deletions

View file

@ -187,6 +187,9 @@ Lock objects have the following methods:
.. versionchanged:: 3.2 .. versionchanged:: 3.2
Lock acquires can now be interrupted by signals on POSIX. Lock acquires can now be interrupted by signals on POSIX.
.. versionchanged:: 3.14
Lock acquires can now be interrupted by signals on Windows.
.. method:: lock.release() .. method:: lock.release()
@ -219,12 +222,6 @@ In addition to these methods, lock objects can also be used via the
* Calling :func:`sys.exit` or raising the :exc:`SystemExit` exception is * Calling :func:`sys.exit` or raising the :exc:`SystemExit` exception is
equivalent to calling :func:`_thread.exit`. equivalent to calling :func:`_thread.exit`.
* It is platform-dependent whether the :meth:`~threading.Lock.acquire` method
on a lock can be interrupted (so that the :exc:`KeyboardInterrupt` exception
will happen immediately, rather than only after the lock has been acquired or
the operation has timed out). It can be interrupted on POSIX, but not on
Windows.
* When the main thread exits, it is system defined whether the other threads * When the main thread exits, it is system defined whether the other threads
survive. On most systems, they are killed without executing survive. On most systems, they are killed without executing
:keyword:`try` ... :keyword:`finally` clauses or executing object :keyword:`try` ... :keyword:`finally` clauses or executing object

View file

@ -567,6 +567,9 @@ All methods are executed atomically.
Lock acquisition can now be interrupted by signals on POSIX if the Lock acquisition can now be interrupted by signals on POSIX if the
underlying threading implementation supports it. underlying threading implementation supports it.
.. versionchanged:: 3.14
Lock acquisition can now be interrupted by signals on Windows.
.. method:: release() .. method:: release()

View file

@ -0,0 +1,4 @@
Pressing :kbd:`Ctrl-C` while blocked in :meth:`threading.Lock.acquire`,
:meth:`threading.RLock.acquire`, and :meth:`threading.Thread.join` now
interrupts the function call and raises a :exc:`KeyboardInterrupt` exception
on Windows, similar to how those functions behave on macOS and Linux.

View file

@ -111,15 +111,28 @@ _PySemaphore_PlatformWait(_PySemaphore *sema, PyTime_t timeout)
millis = (DWORD) div; millis = (DWORD) div;
} }
} }
wait = WaitForSingleObjectEx(sema->platform_sem, millis, FALSE);
// NOTE: we wait on the sigint event even in non-main threads to match the
// behavior of the other platforms. Non-main threads will ignore the
// Py_PARK_INTR result.
HANDLE sigint_event = _PyOS_SigintEvent();
HANDLE handles[2] = { sema->platform_sem, sigint_event };
DWORD count = sigint_event != NULL ? 2 : 1;
wait = WaitForMultipleObjects(count, handles, FALSE, millis);
if (wait == WAIT_OBJECT_0) { if (wait == WAIT_OBJECT_0) {
res = Py_PARK_OK; res = Py_PARK_OK;
} }
else if (wait == WAIT_OBJECT_0 + 1) {
ResetEvent(sigint_event);
res = Py_PARK_INTR;
}
else if (wait == WAIT_TIMEOUT) { else if (wait == WAIT_TIMEOUT) {
res = Py_PARK_TIMEOUT; res = Py_PARK_TIMEOUT;
} }
else { else {
res = Py_PARK_INTR; _Py_FatalErrorFormat(__func__,
"unexpected error from semaphore: %u (error: %u)",
wait, GetLastError());
} }
#elif defined(_Py_USE_SEMAPHORES) #elif defined(_Py_USE_SEMAPHORES)
int err; int err;