1919
2020PYBIND11_NAMESPACE_BEGIN (PYBIND11_NAMESPACE)
2121
22- namespace detail {
22+ PYBIND11_NAMESPACE_BEGIN( detail)
2323#if defined(Py_GIL_DISABLED) || defined(PYBIND11_HAS_SUBINTERPRETER_SUPPORT)
2424using atomic_bool = std::atomic_bool;
2525#else
2626using atomic_bool = bool ;
2727#endif
28- } // namespace detail
28+ PYBIND11_NAMESPACE_END ( detail)
2929
3030// Use the `gil_safe_call_once_and_store` class below instead of the naive
3131//
@@ -127,6 +127,7 @@ class gil_safe_call_once_and_store {
127127// subinterpreter has its own separate state. The cached result may not shareable across
128128// interpreters (e.g., imported modules and their members).
129129
130+ PYBIND11_NAMESPACE_BEGIN (detail)
130131struct call_once_storage_base {
131132 call_once_storage_base () = default ;
132133 virtual ~call_once_storage_base () = default ;
@@ -159,9 +160,7 @@ struct call_once_storage : call_once_storage_base {
159160 call_once_storage &operator =(call_once_storage &&) = delete ;
160161};
161162
162- // / Storage map for `gil_safe_call_once_and_store`. Stored in a capsule in the interpreter's state
163- // / dict with proper destructor to ensure cleanup when the interpreter is destroyed.
164- using call_once_storage_map_type = std::unordered_map<const void *, call_once_storage_base *>;
163+ PYBIND11_NAMESPACE_END (detail)
165164
166165# define PYBIND11_CALL_ONCE_STORAGE_MAP_ID PYBIND11_INTERNALS_ID " _call_once_storage_map__"
167166
@@ -178,18 +177,18 @@ class gil_safe_call_once_and_store {
178177 // Multiple threads may enter here, because the GIL is released in the next line and
179178 // CPython API calls in the `fn()` call below may release and reacquire the GIL.
180179 gil_scoped_release gil_rel; // Needed to establish lock ordering.
181- const void *const key = reinterpret_cast < const void *>( this ) ;
180+ const void *const key = this ;
182181 // There can be multiple threads going through here.
183- call_once_storage<T> *value = nullptr ;
182+ storage_type *value = nullptr ;
184183 {
185184 gil_scoped_acquire gil_acq;
186185 // Only one thread will enter here at a time.
187186 auto &storage_map = *get_or_create_call_once_storage_map ();
188187 const auto it = storage_map.find (key);
189188 if (it != storage_map.end ()) {
190- value = static_cast <call_once_storage<T> *>(it->second );
189+ value = static_cast <storage_type *>(it->second );
191190 } else {
192- value = new call_once_storage<T> {};
191+ value = new storage_type {};
193192 storage_map.emplace (key, value);
194193 }
195194 }
@@ -215,9 +214,9 @@ class gil_safe_call_once_and_store {
215214 T *result = last_storage_ptr_;
216215 if (!is_last_storage_valid ()) {
217216 gil_scoped_acquire gil_acq;
218- const void *const key = reinterpret_cast < const void *>( this ) ;
217+ const void *const key = this ;
219218 auto &storage_map = *get_or_create_call_once_storage_map ();
220- auto *value = static_cast <call_once_storage<T> *>(storage_map.at (key));
219+ auto *value = static_cast <storage_type *>(storage_map.at (key));
221220 result = last_storage_ptr_ = reinterpret_cast <T *>(value->storage );
222221 }
223222 assert (result != nullptr );
@@ -231,20 +230,27 @@ class gil_safe_call_once_and_store {
231230 PYBIND11_DTOR_CONSTEXPR ~gil_safe_call_once_and_store () = default ;
232231
233232private:
233+ using storage_base_type = detail::call_once_storage_base;
234+ using storage_type = detail::call_once_storage<T>;
235+ // Use base type pointer for polymorphism
236+ using storage_map_type = std::unordered_map<const void *, storage_base_type *>;
237+
234238 bool is_last_storage_valid () const {
235239 return is_initialized_by_at_least_one_interpreter_
236240 && detail::get_num_interpreters_seen () == 1 ;
237241 }
238242
239- static call_once_storage_map_type *get_or_create_call_once_storage_map () {
243+ // Storage map for `gil_safe_call_once_and_store`. Stored in a capsule in the interpreter's
244+ // state dict with proper destructor to ensure cleanup when the interpreter is destroyed.
245+ static storage_map_type *get_or_create_call_once_storage_map () {
240246 // Preserve any existing Python error state. dict_getitemstringref may clear
241247 // errors or set new ones when the key is not found; we restore the original
242248 // error state when this scope exits.
243249 error_scope err_scope;
244- dict state_dict = detail::get_python_state_dict ();
250+ auto state_dict = reinterpret_borrow<dict>( detail::get_python_state_dict () );
245251 auto storage_map_obj = reinterpret_steal<object>(
246252 detail::dict_getitemstringref (state_dict.ptr (), PYBIND11_CALL_ONCE_STORAGE_MAP_ID));
247- call_once_storage_map_type *storage_map = nullptr ;
253+ storage_map_type *storage_map = nullptr ;
248254 if (storage_map_obj) {
249255 void *raw_ptr = PyCapsule_GetPointer (storage_map_obj.ptr (), /* name=*/ nullptr );
250256 if (!raw_ptr) {
@@ -253,21 +259,22 @@ class gil_safe_call_once_and_store {
253259 " get_or_create_call_once_storage_map() FAILED" );
254260 throw error_already_set ();
255261 }
256- storage_map = reinterpret_cast <call_once_storage_map_type *>(raw_ptr);
262+ storage_map = static_cast <storage_map_type *>(raw_ptr);
257263 } else {
258264 // Use unique_ptr for exception safety: if capsule creation throws,
259265 // the map is automatically deleted.
260- auto storage_map_ptr
261- = std::unique_ptr<call_once_storage_map_type>(new call_once_storage_map_type ());
266+ auto storage_map_ptr = std::unique_ptr<storage_map_type>(new storage_map_type ());
262267 // Create capsule with destructor to clean up the storage map when the interpreter
263268 // shuts down
264269 state_dict[PYBIND11_CALL_ONCE_STORAGE_MAP_ID]
265270 = capsule (storage_map_ptr.get (), [](void *ptr) noexcept {
266- auto *map = reinterpret_cast <call_once_storage_map_type *>(ptr);
267- for (const auto &entry : *map) {
268- delete entry.second ;
271+ auto *map = static_cast <storage_map_type *>(ptr);
272+ while (!map->empty ()) {
273+ auto it = map->begin ();
274+ const auto *storage_ptr = it->second ;
275+ map->erase (it);
276+ delete storage_ptr;
269277 }
270- delete map;
271278 });
272279 // Capsule now owns the storage map, release from unique_ptr
273280 storage_map = storage_map_ptr.release ();
0 commit comments