diff --git a/include/pybind11/gil_safe_call_once.h b/include/pybind11/gil_safe_call_once.h index 25cf59511f..c4ca98adbb 100644 --- a/include/pybind11/gil_safe_call_once.h +++ b/include/pybind11/gil_safe_call_once.h @@ -23,11 +23,15 @@ PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) // // The following alternative avoids both problems: // -// PYBIND11_CONSTINIT static py::gil_safe_call_once_and_store obj_importer; -// auto imported_obj = obj_importer.get_stored([]() { return py::module_::import("module_name"); -// }); +// PYBIND11_CONSTINIT static py::gil_safe_call_once_and_store storage; +// auto &imported_obj = storage // Do NOT make this `static`! +// .call_once_and_store_result([]() { +// return py::module_::import("module_name"); +// }) +// .get_stored(); // -// The `get_stored()` argument is meant to be a callable that makes Python C API calls. +// The `call_once_and_store_result()` argument is meant to be a callable that +// makes Python C API calls. // // `T` can be any C++ type, it does not have to be a Python type. template @@ -35,7 +39,7 @@ class gil_safe_call_once_and_store { public: // PRECONDITION: The GIL must be held when `get_stored()` is called. template - T &get_stored(Callable &&fn) { + gil_safe_call_once_and_store &call_once_and_store_result(Callable &&fn) { if (!is_initialized_.load(std::memory_order_acquire)) { gil_scoped_release gil_rel; std::call_once(once_flag_, [&] { @@ -44,6 +48,12 @@ class gil_safe_call_once_and_store { is_initialized_.store(true, std::memory_order_release); }); } + return *this; + } + + // This must only be called after `call_once_and_store()` was called. + T &get_stored() const { + assert(is_initialized_.load(std::memory_order_relaxed)); PYBIND11_WARNING_PUSH #if !defined(__clang__) && defined(__GNUC__) && __GNUC__ < 5 // Needed for gcc 4.8.5 @@ -57,7 +67,7 @@ class gil_safe_call_once_and_store { PYBIND11_DTOR_CONSTEXPR ~gil_safe_call_once_and_store() = default; private: - alignas(T) char storage_[sizeof(T)] = {}; + alignas(T) mutable char storage_[sizeof(T)] = {}; std::once_flag once_flag_ = {}; std::atomic is_initialized_ = {}; }; diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index c5322cb67b..40f988fdff 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -207,8 +207,8 @@ struct npy_api { }; static npy_api &get() { - PYBIND11_CONSTINIT static gil_safe_call_once_and_store imported_api; - return imported_api.get_stored(lookup); + PYBIND11_CONSTINIT static gil_safe_call_once_and_store storage; + return storage.call_once_and_store_result(lookup).get_stored(); } bool PyArray_Check_(PyObject *obj) const { @@ -645,10 +645,13 @@ class dtype : public object { private: static object &_dtype_from_pep3118() { - PYBIND11_CONSTINIT static gil_safe_call_once_and_store imported_obj; - return imported_obj.get_stored([]() { - return detail::import_numpy_core_submodule("_internal").attr("_dtype_from_pep3118"); - }); + PYBIND11_CONSTINIT static gil_safe_call_once_and_store storage; + return storage + .call_once_and_store_result([]() { + return detail::import_numpy_core_submodule("_internal") + .attr("_dtype_from_pep3118"); + }) + .get_stored(); } dtype strip_padding(ssize_t itemsize) {