@@ -181,8 +181,8 @@ class gil_safe_call_once_and_store {
181181 // There can be multiple threads going through here.
182182 storage_type *value = nullptr ;
183183 {
184- gil_scoped_acquire gil_acq;
185- // Only one thread will enter here at a time .
184+ gil_scoped_acquire gil_acq; // Restore lock ordering.
185+ // This function is thread-safe under free-threading .
186186 value = get_or_create_storage_in_state_dict ();
187187 }
188188 assert (value != nullptr );
@@ -259,72 +259,56 @@ class gil_safe_call_once_and_store {
259259 // First, try to get existing storage (fast path).
260260 {
261261 capsule_obj = detail::dict_getitemstring (state_dict.ptr (), key.c_str ());
262- if (capsule_obj != nullptr ) {
263- // Storage already exists, get the storage pointer from the existing capsule.
264- void *raw_ptr = PyCapsule_GetPointer (capsule_obj, /* name=*/ nullptr );
265- if (!raw_ptr) {
266- raise_from (PyExc_SystemError,
267- " pybind11::gil_safe_call_once_and_store::"
268- " get_or_create_storage_in_state_dict() FAILED "
269- " (get existing)" );
270- throw error_already_set ();
271- }
272- return static_cast <storage_type *>(raw_ptr);
273- }
274- if (PyErr_Occurred ()) {
262+ if (capsule_obj == nullptr && PyErr_Occurred ()) {
275263 throw error_already_set ();
276264 }
265+ // Fallthrough if capsule_obj is nullptr (not found).
266+ // Otherwise, we have found the existing storage (most common case) and return it
267+ // below.
277268 }
278269
279- // Storage doesn't exist yet, create a new one。
280- // Use unique_ptr for exception safety: if capsule creation throws,
281- // the storage is automatically deleted.
282- auto storage_ptr = std::unique_ptr<storage_type>(new storage_type{});
283- // Create capsule with destructor to clean up when the interpreter shuts down.
284- auto new_capsule = capsule (
285- storage_ptr.get (), [](void *ptr) -> void { delete static_cast <storage_type *>(ptr); });
286-
287- // Use `PyDict_SetDefault` for atomic test-and-set:
288- // - If key doesn't exist, inserts our capsule and returns it.
289- // - If key exists (another thread inserted first), returns the existing value.
290- // This is thread-safe because `PyDict_SetDefault` will hold a lock on the dict.
291- //
292- // NOTE: Here we use `dict_setdefaultstring` instead of `dict_setdefaultstringref` because
293- // the capsule is kept alive until interpreter shutdown, so we do not need to handle incref
294- // and decref here.
295- capsule_obj
296- = detail::dict_setdefaultstring (state_dict.ptr (), key.c_str (), new_capsule.ptr ());
297270 if (capsule_obj == nullptr ) {
298- throw error_already_set ();
299- }
300-
301- // Check whether we inserted our new capsule or another thread did.
302- if (capsule_obj == new_capsule.ptr ()) {
303- // We successfully inserted our new capsule, release ownership from unique_ptr.
304- return storage_ptr.release ();
305- }
306- // Another thread already inserted a capsule, use theirs and discard ours.
307- {
308- // Disable the destructor of our unused capsule to prevent double-free:
309- // unique_ptr will clean up the storage on function exit, and the capsule should not.
310- if (PyCapsule_SetDestructor (new_capsule.ptr (), nullptr ) != 0 ) {
311- raise_from (PyExc_SystemError,
312- " pybind11::gil_safe_call_once_and_store::"
313- " get_or_create_storage_in_state_dict() FAILED "
314- " (clear destructor of unused capsule)" );
315- throw error_already_set ();
316- }
317- // Get the storage pointer from the existing capsule.
318- void *raw_ptr = PyCapsule_GetPointer (capsule_obj, /* name=*/ nullptr );
319- if (!raw_ptr) {
320- raise_from (PyExc_SystemError,
321- " pybind11::gil_safe_call_once_and_store::"
322- " get_or_create_storage_in_state_dict() FAILED "
323- " (get after setdefault)" );
271+ // Storage doesn't exist yet, create a new one.
272+ // Use unique_ptr for exception safety: if capsule creation throws, the storage is
273+ // automatically deleted.
274+ auto storage_ptr = std::unique_ptr<storage_type>(new storage_type{});
275+ // Create capsule with destructor to clean up when the interpreter shuts down.
276+ auto new_capsule = capsule (storage_ptr.get (), [](void *ptr) -> void {
277+ delete static_cast <storage_type *>(ptr);
278+ });
279+ // At this point, the capsule object is created successfully.
280+ // Release the unique_ptr and let the capsule object own the storage to avoid
281+ // double-free.
282+ storage_ptr.reset ();
283+
284+ // Use `PyDict_SetDefault` for atomic test-and-set:
285+ // - If key doesn't exist, inserts our capsule and returns it.
286+ // - If key exists (another thread inserted first), returns the existing value.
287+ // This is thread-safe because `PyDict_SetDefault` will hold a lock on the dict.
288+ //
289+ // NOTE: Here we use `dict_setdefaultstring` instead of `dict_setdefaultstringref`
290+ // because the capsule is kept alive until interpreter shutdown, so we do not need to
291+ // handle incref and decref here.
292+ capsule_obj
293+ = detail::dict_setdefaultstring (state_dict.ptr (), key.c_str (), new_capsule.ptr ());
294+ if (capsule_obj == nullptr ) {
324295 throw error_already_set ();
325296 }
326- return static_cast <storage_type *>(raw_ptr);
297+ // - If key already existed, our `new_capsule` is not inserted, it will be destructed
298+ // when going out of scope here, which will also free the storage.
299+ // - Otherwise, our `new_capsule` is now in the dict, and it owns the storage and the
300+ // state dict will incref it.
301+ }
302+
303+ // Get the storage pointer from the capsule.
304+ void *raw_ptr = PyCapsule_GetPointer (capsule_obj, /* name=*/ nullptr );
305+ if (!raw_ptr) {
306+ raise_from (PyExc_SystemError,
307+ " pybind11::gil_safe_call_once_and_store::"
308+ " get_or_create_storage_in_state_dict() FAILED" );
309+ throw error_already_set ();
327310 }
311+ return static_cast <storage_type *>(raw_ptr);
328312 }
329313
330314 // No storage needed when subinterpreter support is enabled.
0 commit comments