Skip to content

Commit 729654c

Browse files
committed
Add new argument to gil_safe_call_once_and_store::call_once_and_store_result
1 parent 1006933 commit 729654c

File tree

1 file changed

+18
-11
lines changed

1 file changed

+18
-11
lines changed

include/pybind11/gil_safe_call_once.h

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,12 @@
88
#include <cassert>
99
#include <mutex>
1010

11-
#ifdef Py_GIL_DISABLED
11+
#if defined(Py_GIL_DISABLED) || defined(PYBIND11_HAS_SUBINTERPRETER_SUPPORT)
1212
# include <atomic>
13+
14+
using atomic_bool = std::atomic_bool;
15+
#else
16+
using atomic_bool = bool;
1317
#endif
1418

1519
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
@@ -53,16 +57,18 @@ class gil_safe_call_once_and_store {
5357
public:
5458
// PRECONDITION: The GIL must be held when `call_once_and_store_result()` is called.
5559
template <typename Callable>
56-
gil_safe_call_once_and_store &call_once_and_store_result(Callable &&fn) {
60+
gil_safe_call_once_and_store &call_once_and_store_result(Callable &&fn,
61+
void (*finalize_fn)(T &) = nullptr) {
5762
if (!is_initialized_) { // This read is guarded by the GIL.
5863
// Multiple threads may enter here, because the GIL is released in the next line and
5964
// CPython API calls in the `fn()` call below may release and reacquire the GIL.
6065
gil_scoped_release gil_rel; // Needed to establish lock ordering.
6166
std::call_once(once_flag_, [&] {
6267
// Only one thread will ever enter here.
6368
gil_scoped_acquire gil_acq;
64-
::new (storage_) T(fn()); // fn may release, but will reacquire, the GIL.
65-
is_initialized_ = true; // This write is guarded by the GIL.
69+
::new (storage_) T(fn()); // fn may release, but will reacquire, the GIL.
70+
finalize_fn_ = finalize_fn; // Store the finalizer.
71+
is_initialized_ = true; // This write is guarded by the GIL.
6672
});
6773
// All threads will observe `is_initialized_` as true here.
6874
}
@@ -83,20 +89,21 @@ class gil_safe_call_once_and_store {
8389
}
8490

8591
constexpr gil_safe_call_once_and_store() = default;
86-
PYBIND11_DTOR_CONSTEXPR ~gil_safe_call_once_and_store() = default;
92+
PYBIND11_DTOR_CONSTEXPR ~gil_safe_call_once_and_store() {
93+
if (is_initialized_ && finalize_fn_ != nullptr) {
94+
finalize_fn_(*reinterpret_cast<T *>(storage_));
95+
}
96+
}
8797

8898
private:
8999
alignas(T) char storage_[sizeof(T)] = {};
90100
std::once_flag once_flag_;
91-
#ifdef Py_GIL_DISABLED
92-
std::atomic_bool
93-
#else
94-
bool
95-
#endif
96-
is_initialized_{false};
101+
void (*finalize_fn_)(T &) = nullptr;
102+
97103
// The `is_initialized_`-`storage_` pair is very similar to `std::optional`,
98104
// but the latter does not have the triviality properties of former,
99105
// therefore `std::optional` is not a viable alternative here.
106+
atomic_bool is_initialized_{false};
100107
};
101108

102109
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)

0 commit comments

Comments
 (0)