Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit cc581f3

Browse files
authored
gh-135099: Only wait on _PyOS_SigintEvent() in main thread (GH-135100)
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.
1 parent 8f778f7 commit cc581f3

File tree

2 files changed

+18
-6
lines changed

2 files changed

+18
-6
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix a crash that could occur on Windows when a background thread waits on a
2+
:c:type:`PyMutex` while the main thread is shutting down the interpreter.

Python/parking_lot.c

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -112,17 +112,27 @@ _PySemaphore_PlatformWait(_PySemaphore *sema, PyTime_t timeout)
112112
}
113113
}
114114

115-
// NOTE: we wait on the sigint event even in non-main threads to match the
116-
// behavior of the other platforms. Non-main threads will ignore the
117-
// Py_PARK_INTR result.
118-
HANDLE sigint_event = _PyOS_SigintEvent();
119-
HANDLE handles[2] = { sema->platform_sem, sigint_event };
120-
DWORD count = sigint_event != NULL ? 2 : 1;
115+
HANDLE handles[2] = { sema->platform_sem, NULL };
116+
HANDLE sigint_event = NULL;
117+
DWORD count = 1;
118+
if (_Py_IsMainThread()) {
119+
// gh-135099: Wait on the SIGINT event only in the main thread. Other
120+
// threads would ignore the result anyways, and accessing
121+
// `_PyOS_SigintEvent()` from non-main threads may race with
122+
// interpreter shutdown, which closes the event handle. Note that
123+
// non-main interpreters will ignore the result.
124+
sigint_event = _PyOS_SigintEvent();
125+
if (sigint_event != NULL) {
126+
handles[1] = sigint_event;
127+
count = 2;
128+
}
129+
}
121130
wait = WaitForMultipleObjects(count, handles, FALSE, millis);
122131
if (wait == WAIT_OBJECT_0) {
123132
res = Py_PARK_OK;
124133
}
125134
else if (wait == WAIT_OBJECT_0 + 1) {
135+
assert(sigint_event != NULL);
126136
ResetEvent(sigint_event);
127137
res = Py_PARK_INTR;
128138
}

0 commit comments

Comments
 (0)