]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-104341: Adjust tstate_must_exit() to Respect Interpreter Finalization (gh-104437)
authorEric Snow <ericsnowcurrently@gmail.com>
Mon, 15 May 2023 19:59:26 +0000 (13:59 -0600)
committerGitHub <noreply@github.com>
Mon, 15 May 2023 19:59:26 +0000 (13:59 -0600)
With the move to a per-interpreter GIL, this check slipped through the cracks.

Include/cpython/pylifecycle.h
Include/internal/pycore_interp.h
Modules/_asynciomodule.c
Modules/_io/bufferedio.c
Modules/_sqlite/connection.c
Modules/_winapi.c
Python/_warnings.c
Python/ceval_gil.c
Python/pylifecycle.c
Python/pystate.c
Python/sysmodule.c

index 08569ee683ce0dc46bcff27f2bc4b68e56801404..314a5cc5b942ac484473ef750df4f6a7177e6e0d 100644 (file)
@@ -52,6 +52,7 @@ PyAPI_FUNC(const char *) _Py_gitidentifier(void);
 PyAPI_FUNC(const char *) _Py_gitversion(void);
 
 PyAPI_FUNC(int) _Py_IsFinalizing(void);
+PyAPI_FUNC(int) _Py_IsInterpreterFinalizing(PyInterpreterState *interp);
 
 /* Random */
 PyAPI_FUNC(int) _PyOS_URandom(void *buffer, Py_ssize_t size);
index 527b2121148f4cbbf9e88b0b3b16d84443e78fd6..edc076fc04f6c35e6c2f8807baae7262787ada6e 100644 (file)
@@ -83,6 +83,13 @@ struct _is {
     int _initialized;
     int finalizing;
 
+    /* Set by Py_EndInterpreter().
+
+       Use _PyInterpreterState_GetFinalizing()
+       and _PyInterpreterState_SetFinalizing()
+       to access it, don't access it directly. */
+    _Py_atomic_address _finalizing;
+
     struct _obmalloc_state obmalloc;
 
     struct _ceval_state ceval;
@@ -191,6 +198,17 @@ struct _is {
 extern void _PyInterpreterState_Clear(PyThreadState *tstate);
 
 
+static inline PyThreadState*
+_PyInterpreterState_GetFinalizing(PyInterpreterState *interp) {
+    return (PyThreadState*)_Py_atomic_load_relaxed(&interp->_finalizing);
+}
+
+static inline void
+_PyInterpreterState_SetFinalizing(PyInterpreterState *interp, PyThreadState *tstate) {
+    _Py_atomic_store_relaxed(&interp->_finalizing, (uintptr_t)tstate);
+}
+
+
 /* cross-interpreter data registry */
 
 /* For now we use a global registry of shareable classes.  An
index 3830245abe87b3a8982c23be55fdf24df89f4142..7e33558dba3e32f53f9f7c9dc588356a55e25fd7 100644 (file)
@@ -526,7 +526,7 @@ future_init(FutureObj *fut, PyObject *loop)
     if (is_true < 0) {
         return -1;
     }
-    if (is_true && !_Py_IsFinalizing()) {
+    if (is_true && !_Py_IsInterpreterFinalizing(PyInterpreterState_Get())) {
         /* Only try to capture the traceback if the interpreter is not being
            finalized.  The original motivation to add a `_Py_IsFinalizing()`
            call was to prevent SIGSEGV when a Future is created in a __del__
index 6f291c3449606441a5a8b7506dad710d26b5c5f2..7a0c516411c73b6bf59257224393ed078d5f4d99 100644 (file)
@@ -293,7 +293,8 @@ _enter_buffered_busy(buffered *self)
                      "reentrant call inside %R", self);
         return 0;
     }
-    relax_locking = _Py_IsFinalizing();
+    PyInterpreterState *interp = PyInterpreterState_Get();
+    relax_locking = _Py_IsInterpreterFinalizing(interp);
     Py_BEGIN_ALLOW_THREADS
     if (!relax_locking)
         st = PyThread_acquire_lock(self->lock, 1);
index 7bbb462ed54dfab6426ed952b61d46ab195b5d7c..5c57a4101c4a695ade5136a59eef76af25000782 100644 (file)
@@ -419,7 +419,7 @@ connection_close(pysqlite_Connection *self)
         {
             /* If close is implicitly called as a result of interpreter
              * tear-down, we must not call back into Python. */
-            if (_Py_IsFinalizing()) {
+            if (_Py_IsInterpreterFinalizing(PyInterpreterState_Get())) {
                 remove_callbacks(self->db);
             }
             (void)connection_exec_stmt(self, "ROLLBACK");
index 473bcb4736e925d5c94754cbce96147383a2e109..1e02dbc1a4bfd13d914edb59c1339e70a9b21efc 100644 (file)
@@ -133,7 +133,7 @@ overlapped_dealloc(OverlappedObject *self)
         {
             /* The operation is no longer pending -- nothing to do. */
         }
-        else if (_Py_IsFinalizing())
+        else if (_Py_IsInterpreterFinalizing(PyInterpreterState_Get()))
         {
             /* The operation is still pending -- give a warning.  This
                will probably only happen on Windows XP. */
index 5644db9a3770cb0f8149df67233829112b513c53..dec658680241ed7749194e2441c25389159d0978 100644 (file)
@@ -198,7 +198,7 @@ get_warnings_attr(PyInterpreterState *interp, PyObject *attr, int try_import)
     PyObject *warnings_module, *obj;
 
     /* don't try to import after the start of the Python finallization */
-    if (try_import && !_Py_IsFinalizing()) {
+    if (try_import && !_Py_IsInterpreterFinalizing(interp)) {
         warnings_module = PyImport_Import(&_Py_ID(warnings));
         if (warnings_module == NULL) {
             /* Fallback to the C implementation if we cannot get
index 42e1436bc9130d75b4cce020c7cbba3e99c14fcb..b9bdb74fcedf32580107bbe70efc4d0318e3051a 100644 (file)
@@ -332,6 +332,9 @@ tstate_must_exit(PyThreadState *tstate)
        After Py_Finalize() has been called, tstate can be a dangling pointer:
        point to PyThreadState freed memory. */
     PyThreadState *finalizing = _PyRuntimeState_GetFinalizing(&_PyRuntime);
+    if (finalizing == NULL) {
+        finalizing = _PyInterpreterState_GetFinalizing(tstate->interp);
+    }
     return (finalizing != NULL && finalizing != tstate);
 }
 
index c5dc0f44a380684784adea1796c7c43cceec31cc..cb87f2c0860110c82378a9db14a7e6b6d243dab2 100644 (file)
@@ -1788,6 +1788,7 @@ Py_FinalizeEx(void)
 
     /* Remaining daemon threads will automatically exit
        when they attempt to take the GIL (ex: PyEval_RestoreThread()). */
+    _PyInterpreterState_SetFinalizing(tstate->interp, tstate);
     _PyRuntimeState_SetFinalizing(runtime, tstate);
     runtime->initialized = 0;
     runtime->core_initialized = 0;
@@ -2142,6 +2143,10 @@ Py_EndInterpreter(PyThreadState *tstate)
         Py_FatalError("not the last thread");
     }
 
+    /* Remaining daemon threads will automatically exit
+       when they attempt to take the GIL (ex: PyEval_RestoreThread()). */
+    _PyInterpreterState_SetFinalizing(interp, tstate);
+
     // XXX Call something like _PyImport_Disable() here?
 
     _PyImport_FiniExternal(tstate->interp);
@@ -2152,6 +2157,18 @@ Py_EndInterpreter(PyThreadState *tstate)
     finalize_interp_delete(tstate->interp);
 }
 
+int
+_Py_IsInterpreterFinalizing(PyInterpreterState *interp)
+{
+    /* We check the runtime first since, in a daemon thread,
+       interp might be dangling pointer. */
+    PyThreadState *finalizing = _PyRuntimeState_GetFinalizing(&_PyRuntime);
+    if (finalizing == NULL) {
+        finalizing = _PyInterpreterState_GetFinalizing(interp);
+    }
+    return finalizing != NULL;
+}
+
 /* Add the __main__ module */
 
 static PyStatus
index 26debf1f88b94a37e8adb496c1324c1c9ccf6092..25e655a20279183ad45a1fbcd5dabb9c1ad53594 100644 (file)
@@ -1436,11 +1436,13 @@ PyThreadState_Clear(PyThreadState *tstate)
 
     if (verbose && tstate->cframe->current_frame != NULL) {
         /* bpo-20526: After the main thread calls
-           _PyRuntimeState_SetFinalizing() in Py_FinalizeEx(), threads must
-           exit when trying to take the GIL. If a thread exit in the middle of
-           _PyEval_EvalFrameDefault(), tstate->frame is not reset to its
-           previous value. It is more likely with daemon threads, but it can
-           happen with regular threads if threading._shutdown() fails
+           _PyInterpreterState_SetFinalizing() in Py_FinalizeEx()
+           (or in Py_EndInterpreter() for subinterpreters),
+           threads must exit when trying to take the GIL.
+           If a thread exit in the middle of _PyEval_EvalFrameDefault(),
+           tstate->frame is not reset to its previous value.
+           It is more likely with daemon threads, but it can happen
+           with regular threads if threading._shutdown() fails
            (ex: interrupted by CTRL+C). */
         fprintf(stderr,
           "PyThreadState_Clear: warning: thread still has a frame\n");
index 894a3e8a98fd8a29e0a0df9bbbfb9e9bd6c16575..c116c40538a21158e32a303605fdf6f9c2ba960d 100644 (file)
@@ -332,6 +332,7 @@ _PySys_ClearAuditHooks(PyThreadState *ts)
     }
 
     _PyRuntimeState *runtime = ts->interp->runtime;
+    /* The hooks are global so we have to check for runtime finalization. */
     PyThreadState *finalizing = _PyRuntimeState_GetFinalizing(runtime);
     assert(finalizing == ts);
     if (finalizing != ts) {
@@ -2039,6 +2040,9 @@ sys__clear_type_cache_impl(PyObject *module)
     Py_RETURN_NONE;
 }
 
+/* Note that, for now, we do not have a per-interpreter equivalent
+  for sys.is_finalizing(). */
+
 /*[clinic input]
 sys.is_finalizing