]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-135099: Only wait on `_PyOS_SigintEvent()` in main thread (GH-135100)
authorSam Gross <colesbury@gmail.com>
Wed, 4 Jun 2025 07:35:56 +0000 (03:35 -0400)
committerGitHub <noreply@github.com>
Wed, 4 Jun 2025 07:35:56 +0000 (09:35 +0200)
On Windows, the `_PyOS_SigintEvent()` event handle is used to interrupt
the main thread when Ctrl-C is pressed. Previously, we also waited on
the event from other threads, but ignored the result. However, this can
race with interpreter shutdown because the main thread closes the handle
in `_PySignal_Fini` and threads may still be running and using mutexes
during interpreter shtudown.

Only use `_PyOS_SigintEvent()` in the main thread in parking_lot.c, like
we do in other places in the CPython codebase.

Misc/NEWS.d/next/Windows/2025-06-03-18-26-54.gh-issue-135099.Q9usKm.rst [new file with mode: 0644]
Python/parking_lot.c

diff --git a/Misc/NEWS.d/next/Windows/2025-06-03-18-26-54.gh-issue-135099.Q9usKm.rst b/Misc/NEWS.d/next/Windows/2025-06-03-18-26-54.gh-issue-135099.Q9usKm.rst
new file mode 100644 (file)
index 0000000..36e70b1
--- /dev/null
@@ -0,0 +1,2 @@
+Fix a crash that could occur on Windows when a background thread waits on a
+:c:type:`PyMutex` while the main thread is shutting down the interpreter.
index 8edf43235942ab314837f9ef50b3501b4e86354e..e896dea02712f2c33568dbc2911f55f1a6af4dda 100644 (file)
@@ -112,17 +112,27 @@ _PySemaphore_PlatformWait(_PySemaphore *sema, PyTime_t timeout)
         }
     }
 
-    // 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;
+    HANDLE handles[2] = { sema->platform_sem, NULL };
+    HANDLE sigint_event = NULL;
+    DWORD count = 1;
+    if (_Py_IsMainThread()) {
+        // gh-135099: Wait on the SIGINT event only in the main thread. Other
+        // threads would ignore the result anyways, and accessing
+        // `_PyOS_SigintEvent()` from non-main threads may race with
+        // interpreter shutdown, which closes the event handle. Note that
+        // non-main interpreters will ignore the result.
+        sigint_event = _PyOS_SigintEvent();
+        if (sigint_event != NULL) {
+            handles[1] = sigint_event;
+            count = 2;
+        }
+    }
     wait = WaitForMultipleObjects(count, handles, FALSE, millis);
     if (wait == WAIT_OBJECT_0) {
         res = Py_PARK_OK;
     }
     else if (wait == WAIT_OBJECT_0 + 1) {
+        assert(sigint_event != NULL);
         ResetEvent(sigint_event);
         res = Py_PARK_INTR;
     }