Skip to content

Commit 5c9ee49

Browse files
gh-99113: A Per-Interpreter GIL! (gh-104210)
This is the culmination of PEP 684 (and of my 8-year long multi-core Python project)! Each subinterpreter may now be created with its own GIL (via Py_NewInterpreterFromConfig()). If not so configured then the interpreter will share with the main interpreter--the status quo since subinterpreters were added decades ago. The main interpreter always has its own GIL and subinterpreters from Py_NewInterpreter() will always share with the main interpreter.
1 parent 942482c commit 5c9ee49

File tree

7 files changed

+24
-52
lines changed

7 files changed

+24
-52
lines changed

Include/internal/pycore_ceval.h

+1-2
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@ struct _ceval_runtime_state;
2121

2222

2323
extern void _Py_FinishPendingCalls(PyThreadState *tstate);
24-
extern void _PyEval_InitRuntimeState(struct _ceval_runtime_state *);
25-
extern void _PyEval_InitState(struct _ceval_state *, PyThread_type_lock);
24+
extern void _PyEval_InitState(PyInterpreterState *, PyThread_type_lock);
2625
extern void _PyEval_FiniState(struct _ceval_state *ceval);
2726
PyAPI_FUNC(void) _PyEval_SignalReceived(PyInterpreterState *interp);
2827
PyAPI_FUNC(int) _PyEval_AddPendingCall(

Include/internal/pycore_ceval_state.h

-3
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,6 @@ struct _ceval_runtime_state {
4949
the main thread of the main interpreter can handle signals: see
5050
_Py_ThreadCanHandleSignals(). */
5151
_Py_atomic_int signals_pending;
52-
53-
/* This is (only) used indirectly through PyInterpreterState.ceval.gil. */
54-
struct _gil_runtime_state gil;
5552
};
5653

5754
#ifdef PY_HAVE_PERF_TRAMPOLINE

Include/internal/pycore_interp.h

+3
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,9 @@ struct _is {
178178
basis. Also see _PyRuntimeState regarding the various mutex fields.
179179
*/
180180

181+
/* The per-interpreter GIL, which might not be used. */
182+
struct _gil_runtime_state _gil;
183+
181184
/* the initial PyInterpreterState.threads.head */
182185
PyThreadState _initial_thread;
183186
};

Include/internal/pycore_runtime.h

-2
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,6 @@ struct _getargs_runtime_state {
3232
struct _PyArg_Parser *static_parsers;
3333
};
3434

35-
/* ceval state */
36-
3735
/* GIL state */
3836

3937
struct _gilstate_runtime_state {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
The GIL is now (optionally) per-interpreter. This is the fundamental change
2+
for PEP 684. This is all made possible by virtue of the isolated state of
3+
each interpreter in the process. The behavior of the main interpreter
4+
remains unchanged. Likewise, interpreters created using
5+
``Py_NewInterpreter()`` are not affected. To get an interpreter with its
6+
own GIL, call ``Py_NewInterpreterFromConfig()``.

Python/ceval_gil.c

+13-42
Original file line numberDiff line numberDiff line change
@@ -464,17 +464,15 @@ take_gil(PyThreadState *tstate)
464464

465465
void _PyEval_SetSwitchInterval(unsigned long microseconds)
466466
{
467-
/* XXX per-interpreter GIL */
468-
PyInterpreterState *interp = _PyInterpreterState_Main();
467+
PyInterpreterState *interp = _PyInterpreterState_Get();
469468
struct _gil_runtime_state *gil = interp->ceval.gil;
470469
assert(gil != NULL);
471470
gil->interval = microseconds;
472471
}
473472

474473
unsigned long _PyEval_GetSwitchInterval(void)
475474
{
476-
/* XXX per-interpreter GIL */
477-
PyInterpreterState *interp = _PyInterpreterState_Main();
475+
PyInterpreterState *interp = _PyInterpreterState_Get();
478476
struct _gil_runtime_state *gil = interp->ceval.gil;
479477
assert(gil != NULL);
480478
return gil->interval;
@@ -484,7 +482,9 @@ unsigned long _PyEval_GetSwitchInterval(void)
484482
int
485483
_PyEval_ThreadsInitialized(void)
486484
{
487-
/* XXX per-interpreter GIL */
485+
/* XXX This is only needed for an assert in PyGILState_Ensure(),
486+
* which currently does not work with subinterpreters.
487+
* Thus we only use the main interpreter. */
488488
PyInterpreterState *interp = _PyInterpreterState_Main();
489489
if (interp == NULL) {
490490
return 0;
@@ -532,27 +532,16 @@ _PyEval_InitGIL(PyThreadState *tstate, int own_gil)
532532
assert(tstate->interp->ceval.gil == NULL);
533533
int locked;
534534
if (!own_gil) {
535+
/* The interpreter will share the main interpreter's instead. */
535536
PyInterpreterState *main_interp = _PyInterpreterState_Main();
536537
assert(tstate->interp != main_interp);
537538
struct _gil_runtime_state *gil = main_interp->ceval.gil;
538539
init_shared_gil(tstate->interp, gil);
539540
locked = current_thread_holds_gil(gil, tstate);
540541
}
541-
/* XXX per-interpreter GIL */
542-
else if (!_Py_IsMainInterpreter(tstate->interp)) {
543-
/* Currently, the GIL is shared by all interpreters,
544-
and only the main interpreter is responsible to create
545-
and destroy it. */
546-
struct _gil_runtime_state *main_gil = _PyInterpreterState_Main()->ceval.gil;
547-
init_shared_gil(tstate->interp, main_gil);
548-
// XXX For now we lie.
549-
tstate->interp->ceval.own_gil = 1;
550-
locked = current_thread_holds_gil(main_gil, tstate);
551-
}
552542
else {
553543
PyThread_init_thread();
554-
// XXX per-interpreter GIL: switch to interp->_gil.
555-
init_own_gil(tstate->interp, &tstate->interp->runtime->ceval.gil);
544+
init_own_gil(tstate->interp, &tstate->interp->_gil);
556545
locked = 0;
557546
}
558547
if (!locked) {
@@ -565,32 +554,22 @@ _PyEval_InitGIL(PyThreadState *tstate, int own_gil)
565554
void
566555
_PyEval_FiniGIL(PyInterpreterState *interp)
567556
{
568-
if (interp->ceval.gil == NULL) {
557+
struct _gil_runtime_state *gil = interp->ceval.gil;
558+
if (gil == NULL) {
569559
/* It was already finalized (or hasn't been initialized yet). */
570560
assert(!interp->ceval.own_gil);
571561
return;
572562
}
573563
else if (!interp->ceval.own_gil) {
574564
#ifdef Py_DEBUG
575565
PyInterpreterState *main_interp = _PyInterpreterState_Main();
576-
assert(interp != main_interp);
566+
assert(main_interp != NULL && interp != main_interp);
577567
assert(interp->ceval.gil == main_interp->ceval.gil);
578568
#endif
579569
interp->ceval.gil = NULL;
580570
return;
581571
}
582572

583-
/* XXX per-interpreter GIL */
584-
struct _gil_runtime_state *gil = &interp->runtime->ceval.gil;
585-
if (!_Py_IsMainInterpreter(interp)) {
586-
/* Currently, the GIL is shared by all interpreters,
587-
and only the main interpreter is responsible to create
588-
and destroy it. */
589-
assert(interp->ceval.gil == gil);
590-
interp->ceval.gil = NULL;
591-
return;
592-
}
593-
594573
if (!gil_created(gil)) {
595574
/* First Py_InitializeFromConfig() call: the GIL doesn't exist
596575
yet: do nothing. */
@@ -974,21 +953,13 @@ Py_MakePendingCalls(void)
974953
return 0;
975954
}
976955

977-
/* The interpreter's recursion limit */
978-
979956
void
980-
_PyEval_InitRuntimeState(struct _ceval_runtime_state *ceval)
957+
_PyEval_InitState(PyInterpreterState *interp, PyThread_type_lock pending_lock)
981958
{
982-
/* XXX per-interpreter GIL */
983-
_gil_initialize(&ceval->gil);
984-
}
959+
_gil_initialize(&interp->_gil);
985960

986-
void
987-
_PyEval_InitState(struct _ceval_state *ceval, PyThread_type_lock pending_lock)
988-
{
989-
struct _pending_calls *pending = &ceval->pending;
961+
struct _pending_calls *pending = &interp->ceval.pending;
990962
assert(pending->lock == NULL);
991-
992963
pending->lock = pending_lock;
993964
}
994965

Python/pystate.c

+1-3
Original file line numberDiff line numberDiff line change
@@ -425,8 +425,6 @@ init_runtime(_PyRuntimeState *runtime,
425425
runtime->open_code_userdata = open_code_userdata;
426426
runtime->audit_hook_head = audit_hook_head;
427427

428-
_PyEval_InitRuntimeState(&runtime->ceval);
429-
430428
PyPreConfig_InitPythonConfig(&runtime->preconfig);
431429

432430
PyThread_type_lock *lockptrs[NUMLOCKS] = {
@@ -682,7 +680,7 @@ init_interpreter(PyInterpreterState *interp,
682680
memcpy(&interp->obmalloc.pools.used, temp, sizeof(temp));
683681
}
684682

685-
_PyEval_InitState(&interp->ceval, pending_lock);
683+
_PyEval_InitState(interp, pending_lock);
686684
_PyGC_InitState(&interp->gc);
687685
PyConfig_InitPythonConfig(&interp->config);
688686
_PyType_InitCache(interp);

0 commit comments

Comments
 (0)