With the move to a per-interpreter GIL, this check slipped through the cracks.
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);
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;
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
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__
"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);
{
/* 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");
{
/* 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. */
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
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);
}
/* 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;
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);
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
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");
}
_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) {
Py_RETURN_NONE;
}
+/* Note that, for now, we do not have a per-interpreter equivalent
+ for sys.is_finalizing(). */
+
/*[clinic input]
sys.is_finalizing