Skip to content

Commit

Permalink
Move code to the right places, rename new class and some variables.
Browse files Browse the repository at this point in the history
  • Loading branch information
Ralf W. Grosse-Kunstleve committed Oct 9, 2023
1 parent 6689b06 commit d965f29
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 52 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ set(PYBIND11_HEADERS
include/pybind11/embed.h
include/pybind11/eval.h
include/pybind11/gil.h
include/pybind11/gil_save_static_initialization.h
include/pybind11/iostream.h
include/pybind11/functional.h
include/pybind11/numpy.h
Expand Down
12 changes: 6 additions & 6 deletions include/pybind11/detail/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,12 +118,12 @@
# endif
#endif

#ifndef PYBIND11_CONSTINIT
# ifdef PYBIND11_CPP20
# define PYBIND11_CONSTINIT constinit
# else
# define PYBIND11_CONSTINIT
# endif
#if defined(PYBIND11_CPP20)
# define PYBIND11_CONSTINIT constinit
# define PYBIND11_DTOR_CONSTEXPR constexpr
#else
# define PYBIND11_CONSTINIT
# define PYBIND11_DTOR_CONSTEXPR
#endif

// Compiler version assertions
Expand Down
66 changes: 66 additions & 0 deletions include/pybind11/gil_save_static_initialization.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright (c) 2023 The pybind Community.

#pragma once

#include "detail/common.h"

#include <cassert>
#include <utility>

PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)

// Use the `gil_save_static_initialization` class below instead of the naive
//
// static auto imported_obj = py::module_::import("module_name"); // BAD, DO NOT USE!
//
// which has two serious issues:
//
// 1. Py_DECREF() calls potentially after the Python interpreter was finalized already, and
// 2. deadlocks in multi-threaded processes.
//
// This alternative will avoid both problems:
//
// PYBIND11_CONSTINIT static py::gil_save_static_initialization<py::object> obj_importer;
// auto imported_obj = obj_importer.get([]() { return py::module_::import("module_name"); });
//
// The `get()` 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.
//
// Main author of this class: jbms@ (original name: LazyInitializeAtLeastOnceDestroyNever)
template <typename T>
class gil_save_static_initialization {
public:
// PRECONDITION: The GIL must be held when `get()` is called.
// It is possible that multiple threads execute `get()` with `initialized_`
// still being false, and thus proceed to execute `initialize()`. This can
// happen if `initialize()` releases and reacquires the GIL internally.
// We accept this, and expect the operation to be both idempotent and cheap.
template <typename Initialize>
T &get(Initialize &&initialize) {
if (!initialized_) {
assert(PyGILState_Check());
auto value = initialize();
if (!initialized_) {
::new (value_storage_) T(std::move(value));
initialized_ = true;
}
}
PYBIND11_WARNING_PUSH
#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ < 5
// Needed for gcc 4.8.5
PYBIND11_WARNING_DISABLE_GCC("-Wstrict-aliasing")
#endif
return *reinterpret_cast<T *>(value_storage_);
PYBIND11_WARNING_POP
}

constexpr gil_save_static_initialization() = default;
PYBIND11_DTOR_CONSTEXPR ~gil_save_static_initialization() = default;

private:
alignas(T) char value_storage_[sizeof(T)] = {};
bool initialized_ = false;
};

PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
51 changes: 5 additions & 46 deletions include/pybind11/numpy.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@

#include "pybind11.h"
#include "complex.h"
#include "gil_save_static_initialization.h"

#include <algorithm>
#include <array>
#include <cassert>
#include <cstdint>
#include <cstdlib>
#include <cstring>
Expand Down Expand Up @@ -43,46 +43,6 @@ class array; // Forward declaration

PYBIND11_NAMESPACE_BEGIN(detail)

// Main author of this class: jbms@
template <typename T>
class LazyInitializeAtLeastOnceDestroyNever {
public:
// PRECONDITION: The GIL must be held when `Get()` is called.
// It is possible that multiple threads execute `Get()` with `initialized_`
// still being false, and thus proceed to execute `initialize()`. This can
// happen if `initialize()` releases and reacquires the GIL internally.
// We accept this, and expect the operation to be both idempotent and cheap.
template <typename Initialize>
T &Get(Initialize &&initialize) {
if (!initialized_) {
assert(PyGILState_Check());
auto value = initialize();
if (!initialized_) {
::new (value_storage_) T(std::move(value));
initialized_ = true;
}
}
PYBIND11_WARNING_PUSH
#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ < 5
// Needed for gcc 4.8.5
PYBIND11_WARNING_DISABLE_GCC("-Wstrict-aliasing")
#endif
return *reinterpret_cast<T *>(value_storage_);
PYBIND11_WARNING_POP
}

constexpr LazyInitializeAtLeastOnceDestroyNever() = default;
#if __cplusplus >= 202002L
constexpr
#endif // C++20
~LazyInitializeAtLeastOnceDestroyNever()
= default;

private:
alignas(T) char value_storage_[sizeof(T)] = {};
bool initialized_ = false;
};

template <>
struct handle_type_name<array> {
static constexpr auto name = const_name("numpy.ndarray");
Expand Down Expand Up @@ -247,8 +207,8 @@ struct npy_api {
};

static npy_api &get() {
PYBIND11_CONSTINIT static LazyInitializeAtLeastOnceDestroyNever<npy_api> api_init;
return api_init.Get(lookup);
PYBIND11_CONSTINIT static gil_save_static_initialization<npy_api> imported_api;
return imported_api.get(lookup);
}

bool PyArray_Check_(PyObject *obj) const {
Expand Down Expand Up @@ -685,9 +645,8 @@ class dtype : public object {

private:
static object &_dtype_from_pep3118() {
PYBIND11_CONSTINIT static detail::LazyInitializeAtLeastOnceDestroyNever<object>
imported_obj;
return imported_obj.Get([]() {
PYBIND11_CONSTINIT static gil_save_static_initialization<object> imported_obj;
return imported_obj.get([]() {
return detail::import_numpy_core_submodule("_internal").attr("_dtype_from_pep3118");
});
}
Expand Down

0 comments on commit d965f29

Please sign in to comment.