diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index 23c38660e..5a60f18b2 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -14,12 +14,14 @@ #include #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -42,6 +44,30 @@ class array; // Forward declaration PYBIND11_NAMESPACE_BEGIN(detail) +// Main author of this class: jbms@ +template +class LazyInitializeAtLeastOnceDestroyNever { +public: + template + T &Get(Initialize &&initialize) { + if (!initialized_) { + assert(PyGILState_Check()); + // Multiple threads may run this concurrently, but that is fine. + auto value = initialize(); // May release and re-acquire the GIL. + if (!initialized_) { // This runs with the GIL held, + new // therefore this is reached only once. + (reinterpret_cast(value_storage_)) T(std::move(value)); + initialized_ = true; + } + } + return *reinterpret_cast(value_storage_); + } + +private: + alignas(T) char value_storage_[sizeof(T)]; + bool initialized_ = false; +}; + template <> struct handle_type_name { static constexpr auto name = const_name("numpy.ndarray"); @@ -206,8 +232,8 @@ struct npy_api { }; static npy_api &get() { - static npy_api api = lookup(); - return api; + static LazyInitializeAtLeastOnceDestroyNever api_init; + return api_init.Get(lookup); } bool PyArray_Check_(PyObject *obj) const { @@ -643,10 +669,11 @@ public: char flags() const { return detail::array_descriptor_proxy(m_ptr)->flags; } private: - static object _dtype_from_pep3118() { - module_ m = detail::import_numpy_core_submodule("_internal"); - static PyObject *obj = m.attr("_dtype_from_pep3118").cast().release().ptr(); - return reinterpret_borrow(obj); + static object &_dtype_from_pep3118() { + static detail::LazyInitializeAtLeastOnceDestroyNever imported_obj; + return imported_obj.Get([]() { + return detail::import_numpy_core_submodule("_internal").attr("_dtype_from_pep3118"); + }); } dtype strip_padding(ssize_t itemsize) {