From 0ccd43cedaf49e8469aee5b95acb62f8bd781236 Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Tue, 3 Jun 2025 18:22:39 +0000 Subject: [PATCH 1/2] gh-135099: Only wait on `_PyOS_SigintEvent()` in main thread 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. --- ...-06-03-18-26-54.gh-issue-135099.Q9usKm.rst | 2 ++ Python/parking_lot.c | 21 +++++++++++++------ 2 files changed, 17 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Windows/2025-06-03-18-26-54.gh-issue-135099.Q9usKm.rst 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 index 00000000000000..36e70b1c0d8cb6 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2025-06-03-18-26-54.gh-issue-135099.Q9usKm.rst @@ -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. diff --git a/Python/parking_lot.c b/Python/parking_lot.c index 8edf43235942ab..871b8b53f90f25 100644 --- a/Python/parking_lot.c +++ b/Python/parking_lot.c @@ -112,17 +112,26 @@ _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 (_PyOS_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. + 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; } From 7c7d96e62514b5daa0314298ab66e7262c5fd102 Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Tue, 3 Jun 2025 18:39:36 +0000 Subject: [PATCH 2/2] Use _Py_IsMainThread() instead --- Python/parking_lot.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Python/parking_lot.c b/Python/parking_lot.c index 871b8b53f90f25..e896dea02712f2 100644 --- a/Python/parking_lot.c +++ b/Python/parking_lot.c @@ -115,11 +115,12 @@ _PySemaphore_PlatformWait(_PySemaphore *sema, PyTime_t timeout) HANDLE handles[2] = { sema->platform_sem, NULL }; HANDLE sigint_event = NULL; DWORD count = 1; - if (_PyOS_IsMainThread()) { + 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. + // `_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;