Skip to content

Commit 25d86cf

Browse files
committed
Directly check if/which interpreter is active before doing CLEAR in destructors.
Py_IsFinalizing only applies to the main interpreter.
1 parent da6e071 commit 25d86cf

1 file changed

Lines changed: 22 additions & 25 deletions

File tree

include/pybind11/detail/internals.h

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,16 @@ inline PyTypeObject *make_default_metaclass();
143143
inline PyObject *make_object_base_type(PyTypeObject *metaclass);
144144
inline void translate_exception(std::exception_ptr p);
145145

146+
inline PyThreadState *get_thread_state_unchecked() {
147+
#if defined(PYPY_VERSION) || defined(GRAALVM_PYTHON)
148+
return PyThreadState_GET();
149+
#elif PY_VERSION_HEX < 0x030D0000
150+
return _PyThreadState_UncheckedGet();
151+
#else
152+
return PyThreadState_GetUnchecked();
153+
#endif
154+
}
155+
146156
// Python loads modules by default with dlopen with the RTLD_LOCAL flag; under libc++ and possibly
147157
// other STLs, this means `typeid(A)` from one module won't equal `typeid(A)` from another module
148158
// even when `A` is the same, non-hidden-visibility type (e.g. from a common include). Under
@@ -195,14 +205,6 @@ struct override_hash {
195205

196206
using instance_map = std::unordered_multimap<const void *, instance *>;
197207

198-
inline bool is_interpreter_alive() {
199-
#if PY_VERSION_HEX < 0x030D0000
200-
return Py_IsInitialized() != 0 || _Py_IsFinalizing() != 0;
201-
#else
202-
return Py_IsInitialized() != 0 || Py_IsFinalizing() != 0;
203-
#endif
204-
}
205-
206208
#ifdef Py_GIL_DISABLED
207209
// Wrapper around PyMutex to provide BasicLockable semantics
208210
class pymutex {
@@ -295,9 +297,7 @@ struct internals {
295297
: static_property_type(make_static_property_type()),
296298
default_metaclass(make_default_metaclass()) {
297299
tstate.set(nullptr); // See PR #5870
298-
PyThreadState *cur_tstate = PyThreadState_Get();
299-
300-
istate = cur_tstate->interp;
300+
istate = PyInterpreterState_Get();
301301
registered_exception_translators.push_front(&translate_exception);
302302
#ifdef Py_GIL_DISABLED
303303
// Scale proportional to the number of cores. 2x is a heuristic to reduce contention.
@@ -320,8 +320,10 @@ struct internals {
320320
// Normally this destructor runs during interpreter finalization and it may DECREF things.
321321
// In odd finalization scenarios it might end up running after the interpreter has
322322
// completely shut down, In that case, we should not decref these objects because pymalloc
323-
// is gone.
324-
if (is_interpreter_alive()) {
323+
// is gone. This also applies across sub-interpreters, we should only DECREF when the
324+
// original owning interpreter is active.
325+
auto *tstate = get_thread_state_unchecked();
326+
if (tstate && tstate->interp == istate) {
325327
Py_CLEAR(instance_base);
326328
Py_CLEAR(default_metaclass);
327329
Py_CLEAR(static_property_type);
@@ -336,20 +338,25 @@ struct internals {
336338
// impact any other modules, because the only things accessing the local internals is the
337339
// module that contains them.
338340
struct local_internals {
341+
local_internals() : istate(PyInterpreterState_Get()) {}
342+
339343
// It should be safe to use fast_type_map here because this entire
340344
// data structure is scoped to our single module, and thus a single
341345
// DSO and single instance of type_info for any particular type.
342346
fast_type_map<type_info *> registered_types_cpp;
343347

344348
std::forward_list<ExceptionTranslator> registered_exception_translators;
345349
PyTypeObject *function_record_py_type = nullptr;
350+
PyInterpreterState *istate = nullptr;
346351

347352
~local_internals() {
348353
// Normally this destructor runs during interpreter finalization and it may DECREF things.
349354
// In odd finalization scenarios it might end up running after the interpreter has
350355
// completely shut down, In that case, we should not decref these objects because pymalloc
351-
// is gone.
352-
if (is_interpreter_alive()) {
356+
// is gone. This also applies across sub-interpreters, we should only DECREF when the
357+
// original owning interpreter is active.
358+
auto *tstate = get_thread_state_unchecked();
359+
if (tstate && tstate->interp == istate) {
353360
Py_CLEAR(function_record_py_type);
354361
}
355362
}
@@ -436,16 +443,6 @@ struct native_enum_record {
436443
"__pybind11_module_local_v" PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) \
437444
PYBIND11_COMPILER_TYPE_LEADING_UNDERSCORE PYBIND11_PLATFORM_ABI_ID "__"
438445

439-
inline PyThreadState *get_thread_state_unchecked() {
440-
#if defined(PYPY_VERSION) || defined(GRAALVM_PYTHON)
441-
return PyThreadState_GET();
442-
#elif PY_VERSION_HEX < 0x030D0000
443-
return _PyThreadState_UncheckedGet();
444-
#else
445-
return PyThreadState_GetUnchecked();
446-
#endif
447-
}
448-
449446
/// We use this to figure out if there are or have been multiple subinterpreters active at any
450447
/// point. This must never go from true to false while any interpreter may be running in any
451448
/// thread!

0 commit comments

Comments
 (0)