Skip to content

Commit b0d0673

Browse files
committed
Fix some thread safety issues regarding interpreter deletion.
1 parent 92cf906 commit b0d0673

2 files changed

Lines changed: 40 additions & 17 deletions

File tree

Include/cpython/pystate.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ typedef uintptr_t PyInterpreterRef;
283283

284284
PyAPI_FUNC(PyInterpreterRef) PyInterpreterRef_Get(void);
285285
PyAPI_FUNC(PyInterpreterRef) PyInterpreterRef_Dup(PyInterpreterRef ref);
286-
PyAPI_FUNC(PyInterpreterRef) PyInterpreterState_AsStrong(PyInterpreterState *interp);
286+
PyAPI_FUNC(int) PyInterpreterState_AsStrong(PyInterpreterState *interp, PyInterpreterRef *strong_ptr);
287287
PyAPI_FUNC(void) PyInterpreterRef_Close(PyInterpreterRef ref);
288288

289289
#define PyInterpreterRef_Close(ref) do { \

Python/pystate.c

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3261,10 +3261,9 @@ void
32613261
PyInterpreterRef_Close(PyInterpreterRef ref)
32623262
{
32633263
PyInterpreterState *interp = ref_as_interp(ref);
3264-
decref_interpreter(ref);
3264+
decref_interpreter(interp);
32653265
}
32663266

3267-
32683267
PyInterpreterWeakRef
32693268
PyInterpreterWeakRef_Get(void)
32703269
{
@@ -3285,33 +3284,57 @@ PyInterpreterWeakRef_Close(PyInterpreterWeakRef wref)
32853284
return;
32863285
}
32873286

3288-
int
3289-
PyInterpreterWeakRef_AsStrong(PyInterpreterWeakRef wref, PyInterpreterRef *strong_ptr)
3287+
static int
3288+
try_acquire_strong_ref(PyInterpreterState *interp, PyInterpreterRef *strong_ptr)
32903289
{
3291-
assert(strong_ptr != NULL);
3292-
int64_t interp_id = wref.id;
3293-
PyInterpreterState *interp = _PyInterpreterState_LookUpIDNoErr(interp_id);
3294-
if (interp == NULL) {
3295-
*strong_ptr = 0;
3296-
return -1;
3297-
}
3298-
HEAD_LOCK(&_PyRuntime); // Prevent deletion
32993290
struct _Py_finalizing_threads *finalizing = &interp->threads.finalizing;
33003291
PyMutex *mutex = &finalizing->mutex;
33013292
PyMutex_Lock(mutex); // Synchronize TOCTOU with the event flag
33023293
if (_PyEvent_IsSet(&finalizing->finished)) {
33033294
/* Interpreter has already finished threads */
3304-
interp = NULL;
3295+
*strong_ptr = 0;
3296+
return -1;
33053297
} else {
33063298
_Py_atomic_add_ssize(&finalizing->countdown, 1);
33073299
}
33083300
PyMutex_Unlock(mutex);
3309-
HEAD_UNLOCK(&_PyRuntime);
33103301
*strong_ptr = (PyInterpreterRef)interp;
3311-
33123302
return 0;
33133303
}
33143304

3305+
int
3306+
PyInterpreterWeakRef_AsStrong(PyInterpreterWeakRef wref, PyInterpreterRef *strong_ptr)
3307+
{
3308+
assert(strong_ptr != NULL);
3309+
int64_t interp_id = wref.id;
3310+
/* Interpreters cannot be deleted while we hold the runtime lock. */
3311+
_PyRuntimeState *runtime = &_PyRuntime;
3312+
HEAD_LOCK(runtime);
3313+
PyInterpreterState *interp = interp_look_up_id(runtime, interp_id);
3314+
if (interp == NULL) {
3315+
HEAD_UNLOCK(runtime);
3316+
*strong_ptr = 0;
3317+
return -1;
3318+
}
3319+
3320+
int res = try_acquire_strong_ref(interp, strong_ptr);
3321+
HEAD_UNLOCK(runtime);
3322+
return res;
3323+
}
3324+
3325+
int
3326+
PyInterpreterState_AsStrong(PyInterpreterState *interp, PyInterpreterRef *strong_ptr)
3327+
{
3328+
assert(interp != NULL);
3329+
assert(strong_ptr != NULL);
3330+
_PyRuntimeState *runtime = &_PyRuntime;
3331+
HEAD_LOCK(runtime);
3332+
int res = try_acquire_strong_ref(interp, strong_ptr);
3333+
HEAD_UNLOCK(runtime);
3334+
3335+
return res;
3336+
}
3337+
33153338
int
33163339
PyThreadState_Ensure(PyInterpreterState *interp)
33173340
{
@@ -3321,5 +3344,5 @@ PyThreadState_Ensure(PyInterpreterState *interp)
33213344
void
33223345
PyThreadState_Release(void)
33233346
{
3324-
return 0;
3347+
return;
33253348
}

0 commit comments

Comments
 (0)