diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index 7445c01e45..6f68461bab 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -381,8 +381,7 @@ #define PYBIND11_CONCAT(first, second) first##second #define PYBIND11_ENSURE_INTERNALS_READY \ { \ - pybind11::detail::get_internals_pp_manager().unref(); \ - pybind11::detail::get_internals(); \ + pybind11::detail::ensure_internals(); \ } #if !defined(GRAALVM_PYTHON) @@ -471,7 +470,6 @@ PyModuleDef_Init should be treated like any other PyObject (so not shared across PYBIND11_CHECK_PYTHON_VERSION \ PYBIND11_PRECOMPILED_CONFIG_GUARD \ try { \ - pybind11::detail::ensure_internals(); \ static ::pybind11::detail::slots_array mod_def_slots \ = ::pybind11::detail::init_slots( \ &PYBIND11_CONCAT(pybind11_exec_, name), ##__VA_ARGS__); \ @@ -494,7 +492,7 @@ PyModuleDef_Init should be treated like any other PyObject (so not shared across static void PYBIND11_CONCAT(pybind11_init_, name)(::pybind11::module_ &); \ int PYBIND11_CONCAT(pybind11_exec_, name)(PyObject * pm) { \ try { \ - pybind11::detail::ensure_internals(); \ + PYBIND11_ENSURE_INTERNALS_READY \ auto m = pybind11::reinterpret_borrow<::pybind11::module_>(pm); \ if (!pybind11::detail::get_cached_module(m.attr("__spec__").attr("name"))) { \ PYBIND11_CONCAT(pybind11_init_, name)(m); \ diff --git a/include/pybind11/detail/internals-inl.h b/include/pybind11/detail/internals-inl.h index ea1998435a..7d93347c5f 100644 --- a/include/pybind11/detail/internals-inl.h +++ b/include/pybind11/detail/internals-inl.h @@ -207,12 +207,17 @@ PYBIND11_INLINE PyObject *get_local_internals_capsule() { } PYBIND11_INLINE void ensure_internals() { - pybind11::detail::get_internals_pp_manager().unref(); #ifdef PYBIND11_HAS_SUBINTERPRETER_SUPPORT if (PyInterpreterState_Get() != PyInterpreterState_Main()) { has_seen_non_main_interpreter() = true; } #endif + /* In an embedded app the main interpreter could be finalized and re-initialized and a pybind11 + * extension module would hold a pointer to a deleted internals. The only way to prevent + * access to that is to re-fetch everything from the state dict here. So we first null it out + * of our global copy and then fetch it (creating it if it does not already exist). */ + pybind11::detail::get_internals_pp_manager().unref(); + pybind11::detail::get_local_internals_pp_manager().unref(); pybind11::detail::get_internals(); } diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 9cb8aee5b6..b2346c545f 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -678,7 +678,8 @@ PyObject *cached_create_module(PyObject *spec, PyModuleDef *); /// the sentinel (0) end slot. using slots_array = std::array; -/// Initialize an array of slots based on the supplied exec slot and options. +/** Initialize an array of slots based on the supplied exec slot and options. This code may not + * assume the GIL and may not use pybind11 internals because this runs early during module init. */ template inline slots_array init_slots(int (*exec_fn)(PyObject *), Options &&...options) noexcept { /* NOTE: slots_array MUST be large enough to hold all possible options. If you add an option