@@ -691,6 +691,8 @@ class internals_pp_manager {
691691 }
692692
693693 void destroy () {
694+ auto &pps_have_created_content_ = pps_have_created_content ();
695+
694696#ifdef PYBIND11_HAS_SUBINTERPRETER_SUPPORT
695697 if (has_seen_non_main_interpreter ()) {
696698 auto *tstate = get_thread_state_unchecked ();
@@ -710,13 +712,16 @@ class internals_pp_manager {
710712 }
711713
712714 static void fail_if_internals_recreated (std::unique_ptr<InternalsType> *pp) {
715+ auto &pps_have_created_content_ = pps_have_created_content ();
716+
713717 // Prevent re-creation of internals after destruction during interpreter shutdown.
714718 // If pybind11 code (e.g., tp_traverse/tp_clear calling py::cast) runs after internals
715719 // have been destroyed, a new empty internals would be created, causing type lookup
716720 // failures. See https://github.com/pybind/pybind11/pull/5958#discussion_r2717645230.
717721 if (pps_have_created_content_.find (pp) != pps_have_created_content_.end ()) {
718722 pybind11_fail (" Reentrant call detected while fetching pybind11 internals!" );
719723 }
724+ // Each pp can only create its internals once. Mark this pp as having created its content.
720725 pps_have_created_content_.insert (pp);
721726 }
722727
@@ -762,7 +767,10 @@ class internals_pp_manager {
762767 std::unique_ptr<InternalsType> *internals_singleton_pp_ = nullptr ;
763768
764769 // Tracks pointer-to-pointers whose internals have been created, to detect re-entrancy.
765- inline static std::unordered_set<void *> pps_have_created_content_{};
770+ static std::unordered_set<void *> &pps_have_created_content () {
771+ static std::unordered_set<void *> value{};
772+ return value;
773+ }
766774};
767775
768776// If We loaded the internals through `state_dict`, our `error_already_set`
0 commit comments