Skip to content

Commit dda5d6e

Browse files
authored
bpo-40226: PyInterpreterState_Delete() deletes pending calls (GH-19436)
PyInterpreterState_New() is now responsible to create pending calls, PyInterpreterState_Delete() now deletes pending calls. * Rename _PyEval_InitThreads() to _PyEval_InitGIL() and rename _PyEval_InitGIL() to _PyEval_FiniGIL(). * _PyEval_InitState() and PyEval_FiniState() now create and delete pending calls. _PyEval_InitState() now returns -1 on memory allocation failure. * Add init_interp_create_gil() helper function: code shared by Py_NewInterpreter() and Py_InitializeFromConfig(). * init_interp_create_gil() now also calls _PyEval_FiniGIL(), _PyEval_InitGIL() and _PyGILState_Init() in subinterpreters, but these functions now do nothing when called from a subinterpreter.
1 parent ac2cfe6 commit dda5d6e

File tree

4 files changed

+109
-52
lines changed

4 files changed

+109
-52
lines changed

Include/internal/pycore_ceval.h

+4-3
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ struct _frame;
1717

1818
extern void _Py_FinishPendingCalls(PyThreadState *tstate);
1919
extern void _PyEval_InitRuntimeState(struct _ceval_runtime_state *);
20-
extern void _PyEval_InitState(struct _ceval_state *);
21-
extern void _PyEval_FiniThreads(PyThreadState *tstate);
20+
extern int _PyEval_InitState(struct _ceval_state *ceval);
21+
extern void _PyEval_FiniState(struct _ceval_state *ceval);
2222
PyAPI_FUNC(void) _PyEval_SignalReceived(PyThreadState *tstate);
2323
PyAPI_FUNC(int) _PyEval_AddPendingCall(
2424
PyThreadState *tstate,
@@ -51,7 +51,8 @@ extern PyObject *_PyEval_EvalCode(
5151
PyObject *name, PyObject *qualname);
5252

5353
extern int _PyEval_ThreadsInitialized(_PyRuntimeState *runtime);
54-
extern PyStatus _PyEval_InitThreads(PyThreadState *tstate);
54+
extern PyStatus _PyEval_InitGIL(PyThreadState *tstate);
55+
extern void _PyEval_FiniGIL(PyThreadState *tstate);
5556

5657
extern void _PyEval_ReleaseLock(PyThreadState *tstate);
5758

Python/ceval.c

+50-31
Original file line numberDiff line numberDiff line change
@@ -275,54 +275,52 @@ PyEval_ThreadsInitialized(void)
275275
}
276276

277277
PyStatus
278-
_PyEval_InitThreads(PyThreadState *tstate)
278+
_PyEval_InitGIL(PyThreadState *tstate)
279279
{
280-
assert(is_tstate_valid(tstate));
280+
if (!_Py_IsMainInterpreter(tstate)) {
281+
/* Currently, the GIL is shared by all interpreters,
282+
and only the main interpreter is responsible to create
283+
and destroy it. */
284+
return _PyStatus_OK();
285+
}
281286

282-
if (_Py_IsMainInterpreter(tstate)) {
283-
struct _gil_runtime_state *gil = &tstate->interp->runtime->ceval.gil;
284-
if (gil_created(gil)) {
285-
return _PyStatus_OK();
286-
}
287+
struct _gil_runtime_state *gil = &tstate->interp->runtime->ceval.gil;
288+
assert(!gil_created(gil));
287289

288-
PyThread_init_thread();
289-
create_gil(gil);
290+
PyThread_init_thread();
291+
create_gil(gil);
290292

291-
take_gil(tstate);
292-
}
293-
294-
struct _pending_calls *pending = &tstate->interp->ceval.pending;
295-
assert(pending->lock == NULL);
296-
pending->lock = PyThread_allocate_lock();
297-
if (pending->lock == NULL) {
298-
return _PyStatus_NO_MEMORY();
299-
}
293+
take_gil(tstate);
300294

295+
assert(gil_created(gil));
301296
return _PyStatus_OK();
302297
}
303298

304299
void
305-
PyEval_InitThreads(void)
300+
_PyEval_FiniGIL(PyThreadState *tstate)
306301
{
307-
/* Do nothing: kept for backward compatibility */
308-
}
302+
if (!_Py_IsMainInterpreter(tstate)) {
303+
/* Currently, the GIL is shared by all interpreters,
304+
and only the main interpreter is responsible to create
305+
and destroy it. */
306+
return;
307+
}
309308

310-
void
311-
_PyEval_FiniThreads(PyThreadState *tstate)
312-
{
313309
struct _gil_runtime_state *gil = &tstate->interp->runtime->ceval.gil;
314310
if (!gil_created(gil)) {
311+
/* First Py_InitializeFromConfig() call: the GIL doesn't exist
312+
yet: do nothing. */
315313
return;
316314
}
317315

318316
destroy_gil(gil);
319317
assert(!gil_created(gil));
318+
}
320319

321-
struct _pending_calls *pending = &tstate->interp->ceval.pending;
322-
if (pending->lock != NULL) {
323-
PyThread_free_lock(pending->lock);
324-
pending->lock = NULL;
325-
}
320+
void
321+
PyEval_InitThreads(void)
322+
{
323+
/* Do nothing: kept for backward compatibility */
326324
}
327325

328326
void
@@ -544,6 +542,10 @@ _PyEval_AddPendingCall(PyThreadState *tstate,
544542
{
545543
struct _pending_calls *pending = &tstate->interp->ceval.pending;
546544

545+
/* Ensure that _PyEval_InitPendingCalls() was called
546+
and that _PyEval_FiniPendingCalls() is not called yet. */
547+
assert(pending->lock != NULL);
548+
547549
PyThread_acquire_lock(pending->lock, WAIT_LOCK);
548550
if (pending->finishing) {
549551
PyThread_release_lock(pending->lock);
@@ -721,10 +723,27 @@ _PyEval_InitRuntimeState(struct _ceval_runtime_state *ceval)
721723
_gil_initialize(&ceval->gil);
722724
}
723725

724-
void
726+
int
725727
_PyEval_InitState(struct _ceval_state *ceval)
726728
{
727-
/* PyInterpreterState_New() initializes ceval to zero */
729+
struct _pending_calls *pending = &ceval->pending;
730+
assert(pending->lock == NULL);
731+
732+
pending->lock = PyThread_allocate_lock();
733+
if (pending->lock == NULL) {
734+
return -1;
735+
}
736+
return 0;
737+
}
738+
739+
void
740+
_PyEval_FiniState(struct _ceval_state *ceval)
741+
{
742+
struct _pending_calls *pending = &ceval->pending;
743+
if (pending->lock != NULL) {
744+
PyThread_free_lock(pending->lock);
745+
pending->lock = NULL;
746+
}
728747
}
729748

730749
int

Python/pylifecycle.c

+33-17
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,31 @@ pycore_init_runtime(_PyRuntimeState *runtime,
523523
}
524524

525525

526+
static PyStatus
527+
init_interp_create_gil(PyThreadState *tstate)
528+
{
529+
PyStatus status;
530+
531+
/* finalize_interp_delete() comment explains why _PyEval_FiniGIL() is
532+
only called here. */
533+
_PyEval_FiniGIL(tstate);
534+
535+
/* Auto-thread-state API */
536+
status = _PyGILState_Init(tstate);
537+
if (_PyStatus_EXCEPTION(status)) {
538+
return status;
539+
}
540+
541+
/* Create the GIL and take it */
542+
status = _PyEval_InitGIL(tstate);
543+
if (_PyStatus_EXCEPTION(status)) {
544+
return status;
545+
}
546+
547+
return _PyStatus_OK();
548+
}
549+
550+
526551
static PyStatus
527552
pycore_create_interpreter(_PyRuntimeState *runtime,
528553
const PyConfig *config,
@@ -544,21 +569,7 @@ pycore_create_interpreter(_PyRuntimeState *runtime,
544569
}
545570
(void) PyThreadState_Swap(tstate);
546571

547-
/* We can't call _PyEval_FiniThreads() in Py_FinalizeEx because
548-
destroying the GIL might fail when it is being referenced from
549-
another running thread (see issue #9901).
550-
Instead we destroy the previously created GIL here, which ensures
551-
that we can call Py_Initialize / Py_FinalizeEx multiple times. */
552-
_PyEval_FiniThreads(tstate);
553-
554-
/* Auto-thread-state API */
555-
status = _PyGILState_Init(tstate);
556-
if (_PyStatus_EXCEPTION(status)) {
557-
return status;
558-
}
559-
560-
/* Create the GIL and the pending calls lock */
561-
status = _PyEval_InitThreads(tstate);
572+
status = init_interp_create_gil(tstate);
562573
if (_PyStatus_EXCEPTION(status)) {
563574
return status;
564575
}
@@ -1323,6 +1334,12 @@ finalize_interp_delete(PyThreadState *tstate)
13231334
_PyGILState_Fini(tstate);
13241335
}
13251336

1337+
/* We can't call _PyEval_FiniGIL() here because destroying the GIL lock can
1338+
fail when it is being awaited by another running daemon thread (see
1339+
bpo-9901). Instead pycore_create_interpreter() destroys the previously
1340+
created GIL, which ensures that Py_Initialize / Py_FinalizeEx can be
1341+
called multiple times. */
1342+
13261343
PyInterpreterState_Delete(tstate->interp);
13271344
}
13281345

@@ -1578,8 +1595,7 @@ new_interpreter(PyThreadState **tstate_p)
15781595
goto error;
15791596
}
15801597

1581-
/* Create the pending calls lock */
1582-
status = _PyEval_InitThreads(tstate);
1598+
status = init_interp_create_gil(tstate);
15831599
if (_PyStatus_EXCEPTION(status)) {
15841600
return status;
15851601
}

Python/pystate.c

+22-1
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,10 @@ PyInterpreterState_New(void)
222222
_PyRuntimeState *runtime = &_PyRuntime;
223223
interp->runtime = runtime;
224224

225-
_PyEval_InitState(&interp->ceval);
225+
if (_PyEval_InitState(&interp->ceval) < 0) {
226+
goto out_of_memory;
227+
}
228+
226229
_PyGC_InitState(&interp->gc);
227230
PyConfig_InitPythonConfig(&interp->config);
228231

@@ -267,6 +270,14 @@ PyInterpreterState_New(void)
267270
interp->audit_hooks = NULL;
268271

269272
return interp;
273+
274+
out_of_memory:
275+
if (tstate != NULL) {
276+
_PyErr_NoMemory(tstate);
277+
}
278+
279+
PyMem_RawFree(interp);
280+
return NULL;
270281
}
271282

272283

@@ -335,6 +346,8 @@ PyInterpreterState_Delete(PyInterpreterState *interp)
335346
struct pyinterpreters *interpreters = &runtime->interpreters;
336347
zapthreads(interp, 0);
337348

349+
_PyEval_FiniState(&interp->ceval);
350+
338351
/* Delete current thread. After this, many C API calls become crashy. */
339352
_PyThreadState_Swap(&runtime->gilstate, NULL);
340353

@@ -352,13 +365,15 @@ PyInterpreterState_Delete(PyInterpreterState *interp)
352365
Py_FatalError("remaining threads");
353366
}
354367
*p = interp->next;
368+
355369
if (interpreters->main == interp) {
356370
interpreters->main = NULL;
357371
if (interpreters->head != NULL) {
358372
Py_FatalError("remaining subinterpreters");
359373
}
360374
}
361375
HEAD_UNLOCK(runtime);
376+
362377
if (interp->id_mutex != NULL) {
363378
PyThread_free_lock(interp->id_mutex);
364379
}
@@ -1198,6 +1213,12 @@ PyThreadState_IsCurrent(PyThreadState *tstate)
11981213
PyStatus
11991214
_PyGILState_Init(PyThreadState *tstate)
12001215
{
1216+
if (!_Py_IsMainInterpreter(tstate)) {
1217+
/* Currently, PyGILState is shared by all interpreters. The main
1218+
* interpreter is responsible to initialize it. */
1219+
return _PyStatus_OK();
1220+
}
1221+
12011222
/* must init with valid states */
12021223
assert(tstate != NULL);
12031224
assert(tstate->interp != NULL);

0 commit comments

Comments
 (0)