]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-99113: A Per-Interpreter GIL! (gh-104210)
authorEric Snow <ericsnowcurrently@gmail.com>
Mon, 8 May 2023 19:15:09 +0000 (13:15 -0600)
committerGitHub <noreply@github.com>
Mon, 8 May 2023 19:15:09 +0000 (13:15 -0600)
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.

Include/internal/pycore_ceval.h
Include/internal/pycore_ceval_state.h
Include/internal/pycore_interp.h
Include/internal/pycore_runtime.h
Misc/NEWS.d/next/Core and Builtins/2023-05-05-12-14-47.gh-issue-99113.-RAdnv.rst [new file with mode: 0644]
Python/ceval_gil.c
Python/pystate.c

index 9fd8571cbc87f430d3effdb19f43dbfd0f0cfb6e..3c8b368bd2af4e42733c7f384c883156431ac180 100644 (file)
@@ -21,8 +21,7 @@ struct _ceval_runtime_state;
 
 
 extern void _Py_FinishPendingCalls(PyThreadState *tstate);
-extern void _PyEval_InitRuntimeState(struct _ceval_runtime_state *);
-extern void _PyEval_InitState(struct _ceval_state *, PyThread_type_lock);
+extern void _PyEval_InitState(PyInterpreterState *, PyThread_type_lock);
 extern void _PyEval_FiniState(struct _ceval_state *ceval);
 PyAPI_FUNC(void) _PyEval_SignalReceived(PyInterpreterState *interp);
 PyAPI_FUNC(int) _PyEval_AddPendingCall(
index 4781dd5735dcf645cea0ca31577e3530279ac130..b352801673c40a38978bb6fc53462cbdae70c097 100644 (file)
@@ -49,9 +49,6 @@ struct _ceval_runtime_state {
        the main thread of the main interpreter can handle signals: see
        _Py_ThreadCanHandleSignals(). */
     _Py_atomic_int signals_pending;
-
-    /* This is (only) used indirectly through PyInterpreterState.ceval.gil. */
-    struct _gil_runtime_state gil;
 };
 
 #ifdef PY_HAVE_PERF_TRAMPOLINE
index 7276ce35ba68f0122ee9214eac3d683d22628c94..527b2121148f4cbbf9e88b0b3b16d84443e78fd6 100644 (file)
@@ -178,6 +178,9 @@ struct _is {
        basis.  Also see _PyRuntimeState regarding the various mutex fields.
        */
 
+    /* The per-interpreter GIL, which might not be used. */
+    struct _gil_runtime_state _gil;
+
     /* the initial PyInterpreterState.threads.head */
     PyThreadState _initial_thread;
 };
index d1b165d0ab9c38f9f6a5e94fb64ec4b649923f0e..6e06e874711bc2b3ccfacc879c7994c5b04480f0 100644 (file)
@@ -32,8 +32,6 @@ struct _getargs_runtime_state {
     struct _PyArg_Parser *static_parsers;
 };
 
-/* ceval state */
-
 /* GIL state */
 
 struct _gilstate_runtime_state {
diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-05-05-12-14-47.gh-issue-99113.-RAdnv.rst b/Misc/NEWS.d/next/Core and Builtins/2023-05-05-12-14-47.gh-issue-99113.-RAdnv.rst
new file mode 100644 (file)
index 0000000..42e26cb
--- /dev/null
@@ -0,0 +1,6 @@
+The GIL is now (optionally) per-interpreter.  This is the fundamental change
+for PEP 684.  This is all made possible by virtue of the isolated state of
+each interpreter in the process.  The behavior of the main interpreter
+remains unchanged.  Likewise, interpreters created using
+``Py_NewInterpreter()`` are not affected.  To get an interpreter with its
+own GIL, call ``Py_NewInterpreterFromConfig()``.
index 9958856bae8019e38f0b2f8adb1e973f8041be17..42e1436bc9130d75b4cce020c7cbba3e99c14fcb 100644 (file)
@@ -464,8 +464,7 @@ _ready:
 
 void _PyEval_SetSwitchInterval(unsigned long microseconds)
 {
-    /* XXX per-interpreter GIL */
-    PyInterpreterState *interp = _PyInterpreterState_Main();
+    PyInterpreterState *interp = _PyInterpreterState_Get();
     struct _gil_runtime_state *gil = interp->ceval.gil;
     assert(gil != NULL);
     gil->interval = microseconds;
@@ -473,8 +472,7 @@ void _PyEval_SetSwitchInterval(unsigned long microseconds)
 
 unsigned long _PyEval_GetSwitchInterval(void)
 {
-    /* XXX per-interpreter GIL */
-    PyInterpreterState *interp = _PyInterpreterState_Main();
+    PyInterpreterState *interp = _PyInterpreterState_Get();
     struct _gil_runtime_state *gil = interp->ceval.gil;
     assert(gil != NULL);
     return gil->interval;
@@ -484,7 +482,9 @@ unsigned long _PyEval_GetSwitchInterval(void)
 int
 _PyEval_ThreadsInitialized(void)
 {
-    /* XXX per-interpreter GIL */
+    /* XXX This is only needed for an assert in PyGILState_Ensure(),
+     * which currently does not work with subinterpreters.
+     * Thus we only use the main interpreter. */
     PyInterpreterState *interp = _PyInterpreterState_Main();
     if (interp == NULL) {
         return 0;
@@ -532,27 +532,16 @@ _PyEval_InitGIL(PyThreadState *tstate, int own_gil)
     assert(tstate->interp->ceval.gil == NULL);
     int locked;
     if (!own_gil) {
+        /* The interpreter will share the main interpreter's instead. */
         PyInterpreterState *main_interp = _PyInterpreterState_Main();
         assert(tstate->interp != main_interp);
         struct _gil_runtime_state *gil = main_interp->ceval.gil;
         init_shared_gil(tstate->interp, gil);
         locked = current_thread_holds_gil(gil, tstate);
     }
-    /* XXX per-interpreter GIL */
-    else if (!_Py_IsMainInterpreter(tstate->interp)) {
-        /* Currently, the GIL is shared by all interpreters,
-           and only the main interpreter is responsible to create
-           and destroy it. */
-        struct _gil_runtime_state *main_gil = _PyInterpreterState_Main()->ceval.gil;
-        init_shared_gil(tstate->interp, main_gil);
-        // XXX For now we lie.
-        tstate->interp->ceval.own_gil = 1;
-        locked = current_thread_holds_gil(main_gil, tstate);
-    }
     else {
         PyThread_init_thread();
-        // XXX per-interpreter GIL: switch to interp->_gil.
-        init_own_gil(tstate->interp, &tstate->interp->runtime->ceval.gil);
+        init_own_gil(tstate->interp, &tstate->interp->_gil);
         locked = 0;
     }
     if (!locked) {
@@ -565,7 +554,8 @@ _PyEval_InitGIL(PyThreadState *tstate, int own_gil)
 void
 _PyEval_FiniGIL(PyInterpreterState *interp)
 {
-    if (interp->ceval.gil == NULL) {
+    struct _gil_runtime_state *gil = interp->ceval.gil;
+    if (gil == NULL) {
         /* It was already finalized (or hasn't been initialized yet). */
         assert(!interp->ceval.own_gil);
         return;
@@ -573,24 +563,13 @@ _PyEval_FiniGIL(PyInterpreterState *interp)
     else if (!interp->ceval.own_gil) {
 #ifdef Py_DEBUG
         PyInterpreterState *main_interp = _PyInterpreterState_Main();
-        assert(interp != main_interp);
+        assert(main_interp != NULL && interp != main_interp);
         assert(interp->ceval.gil == main_interp->ceval.gil);
 #endif
         interp->ceval.gil = NULL;
         return;
     }
 
-    /* XXX per-interpreter GIL */
-    struct _gil_runtime_state *gil = &interp->runtime->ceval.gil;
-    if (!_Py_IsMainInterpreter(interp)) {
-        /* Currently, the GIL is shared by all interpreters,
-           and only the main interpreter is responsible to create
-           and destroy it. */
-        assert(interp->ceval.gil == gil);
-        interp->ceval.gil = NULL;
-        return;
-    }
-
     if (!gil_created(gil)) {
         /* First Py_InitializeFromConfig() call: the GIL doesn't exist
            yet: do nothing. */
@@ -974,21 +953,13 @@ Py_MakePendingCalls(void)
     return 0;
 }
 
-/* The interpreter's recursion limit */
-
 void
-_PyEval_InitRuntimeState(struct _ceval_runtime_state *ceval)
+_PyEval_InitState(PyInterpreterState *interp, PyThread_type_lock pending_lock)
 {
-    /* XXX per-interpreter GIL */
-    _gil_initialize(&ceval->gil);
-}
+    _gil_initialize(&interp->_gil);
 
-void
-_PyEval_InitState(struct _ceval_state *ceval, PyThread_type_lock pending_lock)
-{
-    struct _pending_calls *pending = &ceval->pending;
+    struct _pending_calls *pending = &interp->ceval.pending;
     assert(pending->lock == NULL);
-
     pending->lock = pending_lock;
 }
 
index f14934361dab78806ae5d2dc2916c7b85df38c86..26debf1f88b94a37e8adb496c1324c1c9ccf6092 100644 (file)
@@ -425,8 +425,6 @@ init_runtime(_PyRuntimeState *runtime,
     runtime->open_code_userdata = open_code_userdata;
     runtime->audit_hook_head = audit_hook_head;
 
-    _PyEval_InitRuntimeState(&runtime->ceval);
-
     PyPreConfig_InitPythonConfig(&runtime->preconfig);
 
     PyThread_type_lock *lockptrs[NUMLOCKS] = {
@@ -682,7 +680,7 @@ init_interpreter(PyInterpreterState *interp,
         memcpy(&interp->obmalloc.pools.used, temp, sizeof(temp));
     }
 
-    _PyEval_InitState(&interp->ceval, pending_lock);
+    _PyEval_InitState(interp, pending_lock);
     _PyGC_InitState(&interp->gc);
     PyConfig_InitPythonConfig(&interp->config);
     _PyType_InitCache(interp);