Skip to content

Commit 8479a34

Browse files
bpo-33608: Make sure locks in the runtime are properly re-created. (gh-12245)
1 parent 5be45a6 commit 8479a34

File tree

4 files changed

+40
-40
lines changed

4 files changed

+40
-40
lines changed

Include/internal/pycore_pystate.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ typedef struct pyruntimestate {
229229
PyAPI_DATA(_PyRuntimeState) _PyRuntime;
230230
PyAPI_FUNC(_PyInitError) _PyRuntimeState_Init(_PyRuntimeState *);
231231
PyAPI_FUNC(void) _PyRuntimeState_Fini(_PyRuntimeState *);
232+
PyAPI_FUNC(void) _PyRuntimeState_ReInitThreads(void);
232233

233234
/* Initialize _PyRuntimeState.
234235
Return NULL on success, or return an error message on failure. */

Modules/posixmodule.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,7 @@ PyOS_AfterFork_Child(void)
429429
PyEval_ReInitThreads();
430430
_PyImport_ReInitLock();
431431
_PySignal_AfterFork();
432+
_PyRuntimeState_ReInitThreads();
432433

433434
run_at_forkers(_PyInterpreterState_Get()->after_forkers_child, 0);
434435
}

Python/ceval.c

Lines changed: 10 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -174,10 +174,10 @@ PyEval_InitThreads(void)
174174
PyThread_init_thread();
175175
create_gil();
176176
take_gil(_PyThreadState_GET());
177-
// Set it to the ID of the main thread of the main interpreter.
178-
_PyRuntime.main_thread = PyThread_get_thread_ident();
179-
if (!_PyRuntime.ceval.pending.lock) {
180-
_PyRuntime.ceval.pending.lock = PyThread_allocate_lock();
177+
178+
_PyRuntime.ceval.pending.lock = PyThread_allocate_lock();
179+
if (_PyRuntime.ceval.pending.lock == NULL) {
180+
return Py_FatalError("Can't initialize threads for pending calls");
181181
}
182182
}
183183

@@ -246,8 +246,11 @@ PyEval_ReInitThreads(void)
246246
return;
247247
recreate_gil();
248248
take_gil(current_tstate);
249-
_PyRuntime.main_thread = PyThread_get_thread_ident();
249+
250250
_PyRuntime.ceval.pending.lock = PyThread_allocate_lock();
251+
if (_PyRuntime.ceval.pending.lock == NULL) {
252+
Py_FatalError("Can't initialize threads for pending calls");
253+
}
251254

252255
/* Destroy all threads except the current one */
253256
_PyThreadState_DeleteExcept(current_tstate);
@@ -362,35 +365,12 @@ _pop_pending_call(int (**func)(void *), void **arg)
362365
int
363366
Py_AddPendingCall(int (*func)(void *), void *arg)
364367
{
365-
PyThread_type_lock lock = _PyRuntime.ceval.pending.lock;
366-
367-
/* try a few times for the lock. Since this mechanism is used
368-
* for signal handling (on the main thread), there is a (slim)
369-
* chance that a signal is delivered on the same thread while we
370-
* hold the lock during the Py_MakePendingCalls() function.
371-
* This avoids a deadlock in that case.
372-
* Note that signals can be delivered on any thread. In particular,
373-
* on Windows, a SIGINT is delivered on a system-created worker
374-
* thread.
375-
* We also check for lock being NULL, in the unlikely case that
376-
* this function is called before any bytecode evaluation takes place.
377-
*/
378-
if (lock != NULL) {
379-
int i;
380-
for (i = 0; i<100; i++) {
381-
if (PyThread_acquire_lock(lock, NOWAIT_LOCK))
382-
break;
383-
}
384-
if (i == 100)
385-
return -1;
386-
}
387-
368+
PyThread_acquire_lock(_PyRuntime.ceval.pending.lock, WAIT_LOCK);
388369
int result = _push_pending_call(func, arg);
370+
PyThread_release_lock(_PyRuntime.ceval.pending.lock);
389371

390372
/* signal main loop */
391373
SIGNAL_PENDING_CALLS();
392-
if (lock != NULL)
393-
PyThread_release_lock(lock);
394374
return result;
395375
}
396376

@@ -439,15 +419,6 @@ make_pending_calls(void)
439419
UNSIGNAL_PENDING_CALLS();
440420
int res = 0;
441421

442-
if (!_PyRuntime.ceval.pending.lock) {
443-
/* initial allocation of the lock */
444-
_PyRuntime.ceval.pending.lock = PyThread_allocate_lock();
445-
if (_PyRuntime.ceval.pending.lock == NULL) {
446-
res = -1;
447-
goto error;
448-
}
449-
}
450-
451422
/* perform a bounded number of calls, in case of recursion */
452423
for (int i=0; i<NPENDINGCALLS; i++) {
453424
int (*func)(void *) = NULL;

Python/pystate.c

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ _PyRuntimeState_Init_impl(_PyRuntimeState *runtime)
6060
return _Py_INIT_ERR("Can't initialize threads for cross-interpreter data registry");
6161
}
6262

63-
// runtime->main_thread is set in PyEval_InitThreads().
63+
// Set it to the ID of the main thread of the main interpreter.
64+
runtime->main_thread = PyThread_get_thread_ident();
6465

6566
return _Py_INIT_OK();
6667
}
@@ -94,6 +95,32 @@ _PyRuntimeState_Fini(_PyRuntimeState *runtime)
9495
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
9596
}
9697

98+
/* This function is called from PyOS_AfterFork_Child to ensure that
99+
* newly created child processes do not share locks with the parent.
100+
*/
101+
102+
void
103+
_PyRuntimeState_ReInitThreads(void)
104+
{
105+
// This was initially set in _PyRuntimeState_Init().
106+
_PyRuntime.main_thread = PyThread_get_thread_ident();
107+
108+
_PyRuntime.interpreters.mutex = PyThread_allocate_lock();
109+
if (_PyRuntime.interpreters.mutex == NULL) {
110+
Py_FatalError("Can't initialize lock for runtime interpreters");
111+
}
112+
113+
_PyRuntime.interpreters.main->id_mutex = PyThread_allocate_lock();
114+
if (_PyRuntime.interpreters.main->id_mutex == NULL) {
115+
Py_FatalError("Can't initialize ID lock for main interpreter");
116+
}
117+
118+
_PyRuntime.xidregistry.mutex = PyThread_allocate_lock();
119+
if (_PyRuntime.xidregistry.mutex == NULL) {
120+
Py_FatalError("Can't initialize lock for cross-interpreter data registry");
121+
}
122+
}
123+
97124
#define HEAD_LOCK() PyThread_acquire_lock(_PyRuntime.interpreters.mutex, \
98125
WAIT_LOCK)
99126
#define HEAD_UNLOCK() PyThread_release_lock(_PyRuntime.interpreters.mutex)

0 commit comments

Comments
 (0)