gil_safe_call_once.h
Go to the documentation of this file.
1 // Copyright (c) 2023 The pybind Community.
2 
3 #pragma once
4 
5 #include "detail/common.h"
6 #include "gil.h"
7 
8 #include <cassert>
9 #include <mutex>
10 
12 
13 // Use the `gil_safe_call_once_and_store` class below instead of the naive
14 //
15 // static auto imported_obj = py::module_::import("module_name"); // BAD, DO NOT USE!
16 //
17 // which has two serious issues:
18 //
19 // 1. Py_DECREF() calls potentially after the Python interpreter was finalized already, and
20 // 2. deadlocks in multi-threaded processes (because of missing lock ordering).
21 //
22 // The following alternative avoids both problems:
23 //
24 // PYBIND11_CONSTINIT static py::gil_safe_call_once_and_store<py::object> storage;
25 // auto &imported_obj = storage // Do NOT make this `static`!
26 // .call_once_and_store_result([]() {
27 // return py::module_::import("module_name");
28 // })
29 // .get_stored();
30 //
31 // The parameter of `call_once_and_store_result()` must be callable. It can make
32 // CPython API calls, and in particular, it can temporarily release the GIL.
33 //
34 // `T` can be any C++ type, it does not have to involve CPython API types.
35 //
36 // The behavior with regard to signals, e.g. `SIGINT` (`KeyboardInterrupt`),
37 // is not ideal. If the main thread is the one to actually run the `Callable`,
38 // then a `KeyboardInterrupt` will interrupt it if it is running normal Python
39 // code. The situation is different if a non-main thread runs the
40 // `Callable`, and then the main thread starts waiting for it to complete:
41 // a `KeyboardInterrupt` will not interrupt the non-main thread, but it will
42 // get processed only when it is the main thread's turn again and it is running
43 // normal Python code. However, this will be unnoticeable for quick call-once
44 // functions, which is usually the case.
45 template <typename T>
47 public:
48  // PRECONDITION: The GIL must be held when `call_once_and_store_result()` is called.
49  template <typename Callable>
51  if (!is_initialized_) { // This read is guarded by the GIL.
52  // Multiple threads may enter here, because the GIL is released in the next line and
53  // CPython API calls in the `fn()` call below may release and reacquire the GIL.
54  gil_scoped_release gil_rel; // Needed to establish lock ordering.
55  std::call_once(once_flag_, [&] {
56  // Only one thread will ever enter here.
57  gil_scoped_acquire gil_acq;
58  ::new (storage_) T(fn()); // fn may release, but will reacquire, the GIL.
59  is_initialized_ = true; // This write is guarded by the GIL.
60  });
61  // All threads will observe `is_initialized_` as true here.
62  }
63  // Intentionally not returning `T &` to ensure the calling code is self-documenting.
64  return *this;
65  }
66 
67  // This must only be called after `call_once_and_store_result()` was called.
69  assert(is_initialized_);
70  PYBIND11_WARNING_PUSH
71 #if !defined(__clang__) && defined(__GNUC__) && __GNUC__ < 5
72  // Needed for gcc 4.8.5
73  PYBIND11_WARNING_DISABLE_GCC("-Wstrict-aliasing")
74 #endif
75  return *reinterpret_cast<T *>(storage_);
77  }
78 
79  constexpr gil_safe_call_once_and_store() = default;
81 
82 private:
83  alignas(T) char storage_[sizeof(T)] = {};
84  std::once_flag once_flag_ = {};
85  bool is_initialized_ = false;
86  // The `is_initialized_`-`storage_` pair is very similar to `std::optional`,
87  // but the latter does not have the triviality properties of former,
88  // therefore `std::optional` is not a viable alternative here.
89 };
90 
gil_safe_call_once_and_store::gil_safe_call_once_and_store
constexpr gil_safe_call_once_and_store()=default
PYBIND11_WARNING_DISABLE_GCC
#define PYBIND11_WARNING_DISABLE_GCC(name)
Definition: wrap/pybind11/include/pybind11/detail/common.h:67
gil_safe_call_once_and_store::~gil_safe_call_once_and_store
PYBIND11_DTOR_CONSTEXPR ~gil_safe_call_once_and_store()=default
fn
static double fn[10]
Definition: fresnl.c:103
PYBIND11_WARNING_POP
PYBIND11_WARNING_PUSH PYBIND11_WARNING_POP
Definition: tensor.h:30
gil_safe_call_once_and_store::once_flag_
std::once_flag once_flag_
Definition: gil_safe_call_once.h:84
PYBIND11_NAMESPACE_END
#define PYBIND11_NAMESPACE_END(name)
Definition: wrap/pybind11/include/pybind11/detail/common.h:80
T
Eigen::Triplet< double > T
Definition: Tutorial_sparse_example.cpp:6
PYBIND11_NAMESPACE_BEGIN
#define PYBIND11_NAMESPACE_BEGIN(name)
Definition: wrap/pybind11/include/pybind11/detail/common.h:76
gil_scoped_release
Definition: gil.h:138
gil.h
gil_safe_call_once_and_store::is_initialized_
bool is_initialized_
Definition: gil_safe_call_once.h:85
PYBIND11_NAMESPACE
Definition: test_custom_type_casters.cpp:24
Eigen::Triplet< double >
common.h
gil_scoped_acquire
Definition: gil.h:53
gil_safe_call_once_and_store
Definition: gil_safe_call_once.h:46
gil_safe_call_once_and_store::get_stored
T & get_stored()
Definition: gil_safe_call_once.h:68
gil_safe_call_once_and_store::call_once_and_store_result
gil_safe_call_once_and_store & call_once_and_store_result(Callable &&fn)
Definition: gil_safe_call_once.h:50
PYBIND11_DTOR_CONSTEXPR
#define PYBIND11_DTOR_CONSTEXPR
Definition: wrap/pybind11/include/pybind11/detail/common.h:126
Callable
Definition: typing.h:59
gil_safe_call_once_and_store::storage_
char storage_[sizeof(T)]
Definition: gil_safe_call_once.h:83


gtsam
Author(s):
autogenerated on Fri Oct 4 2024 03:02:08