Skip to content

Commit 7d8339e

Browse files
committed
Try fix thread-safety
1 parent f6d0f88 commit 7d8339e

File tree

2 files changed

+16
-16
lines changed

2 files changed

+16
-16
lines changed

include/pybind11/detail/internals.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -245,8 +245,8 @@ struct call_once_storage_base {
245245

246246
template <typename T>
247247
struct call_once_storage : call_once_storage_base {
248-
void (*finalize)(T &) = nullptr;
249248
alignas(T) char storage[sizeof(T)] = {0};
249+
void (*finalize)(T &) = nullptr;
250250
std::atomic_bool is_initialized{false};
251251

252252
call_once_storage() = default;
@@ -337,7 +337,7 @@ struct internals {
337337
internals &operator=(const internals &other) = delete;
338338
internals &operator=(internals &&other) = delete;
339339
~internals() {
340-
for (auto &entry : call_once_storage_map) {
340+
for (const auto &entry : call_once_storage_map) {
341341
delete entry.second;
342342
}
343343
call_once_storage_map.clear();

include/pybind11/gil_safe_call_once.h

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -131,22 +131,22 @@ class gil_safe_call_once_and_store {
131131
gil_scoped_release gil_rel; // Needed to establish lock ordering.
132132
{
133133
detail::with_internals([&](detail::internals &internals) {
134-
// The concurrency control is done inside `detail::with_internals`.
135-
// At most one thread will enter here at a time.
136-
const void *k = reinterpret_cast<const void *>(this);
134+
const void *key = reinterpret_cast<const void *>(this);
137135
auto &storage_map = internals.call_once_storage_map;
138-
// There can be multiple threads going through here, but only one each at a
139-
// time. So only one thread will create the storage. Other threads will find it
140-
// already created.
141-
auto it = storage_map.find(k);
142-
if (it == storage_map.end()) {
136+
// There can be multiple threads going through here.
137+
if (storage_map.find(key) == storage_map.end()) {
143138
gil_scoped_acquire gil_acq;
144-
auto v = new detail::call_once_storage<T>{};
145-
::new (v->storage) T(fn()); // fn may release, but will reacquire, the GIL.
146-
v->finalize = finalize_fn;
147-
last_storage_ptr_ = reinterpret_cast<T *>(v->storage);
148-
v->is_initialized = true;
149-
storage_map.emplace(k, v);
139+
// Only one thread will enter here at a time.
140+
// Fast recheck to avoid double work.
141+
if (storage_map.find(key) == storage_map.end()) {
142+
auto value = new detail::call_once_storage<T>{};
143+
// fn may release, but will reacquire, the GIL.
144+
::new (value->storage) T(fn());
145+
value->finalize = finalize_fn;
146+
value->is_initialized = true;
147+
storage_map.emplace(key, value);
148+
last_storage_ptr_ = reinterpret_cast<T *>(value->storage);
149+
}
150150
}
151151
is_initialized_by_atleast_one_interpreter_ = true;
152152
});

0 commit comments

Comments
 (0)