Back to home page

EIC code displayed by LXR

 
 

    


Warning, file /include/pybind11/gil_safe_call_once.h was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 // Copyright (c) 2023 The pybind Community.
0002 
0003 #pragma once
0004 
0005 #include "detail/common.h"
0006 #include "gil.h"
0007 
0008 #include <cassert>
0009 #include <mutex>
0010 
0011 #ifdef Py_GIL_DISABLED
0012 #    include <atomic>
0013 #endif
0014 
0015 PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
0016 
0017 // Use the `gil_safe_call_once_and_store` class below instead of the naive
0018 //
0019 //   static auto imported_obj = py::module_::import("module_name"); // BAD, DO NOT USE!
0020 //
0021 // which has two serious issues:
0022 //
0023 //     1. Py_DECREF() calls potentially after the Python interpreter was finalized already, and
0024 //     2. deadlocks in multi-threaded processes (because of missing lock ordering).
0025 //
0026 // The following alternative avoids both problems:
0027 //
0028 //   PYBIND11_CONSTINIT static py::gil_safe_call_once_and_store<py::object> storage;
0029 //   auto &imported_obj = storage // Do NOT make this `static`!
0030 //       .call_once_and_store_result([]() {
0031 //           return py::module_::import("module_name");
0032 //       })
0033 //       .get_stored();
0034 //
0035 // The parameter of `call_once_and_store_result()` must be callable. It can make
0036 // CPython API calls, and in particular, it can temporarily release the GIL.
0037 //
0038 // `T` can be any C++ type, it does not have to involve CPython API types.
0039 //
0040 // The behavior with regard to signals, e.g. `SIGINT` (`KeyboardInterrupt`),
0041 // is not ideal. If the main thread is the one to actually run the `Callable`,
0042 // then a `KeyboardInterrupt` will interrupt it if it is running normal Python
0043 // code. The situation is different if a non-main thread runs the
0044 // `Callable`, and then the main thread starts waiting for it to complete:
0045 // a `KeyboardInterrupt` will not interrupt the non-main thread, but it will
0046 // get processed only when it is the main thread's turn again and it is running
0047 // normal Python code. However, this will be unnoticeable for quick call-once
0048 // functions, which is usually the case.
0049 template <typename T>
0050 class gil_safe_call_once_and_store {
0051 public:
0052     // PRECONDITION: The GIL must be held when `call_once_and_store_result()` is called.
0053     template <typename Callable>
0054     gil_safe_call_once_and_store &call_once_and_store_result(Callable &&fn) {
0055         if (!is_initialized_) { // This read is guarded by the GIL.
0056             // Multiple threads may enter here, because the GIL is released in the next line and
0057             // CPython API calls in the `fn()` call below may release and reacquire the GIL.
0058             gil_scoped_release gil_rel; // Needed to establish lock ordering.
0059             std::call_once(once_flag_, [&] {
0060                 // Only one thread will ever enter here.
0061                 gil_scoped_acquire gil_acq;
0062                 ::new (storage_) T(fn()); // fn may release, but will reacquire, the GIL.
0063                 is_initialized_ = true;   // This write is guarded by the GIL.
0064             });
0065             // All threads will observe `is_initialized_` as true here.
0066         }
0067         // Intentionally not returning `T &` to ensure the calling code is self-documenting.
0068         return *this;
0069     }
0070 
0071     // This must only be called after `call_once_and_store_result()` was called.
0072     T &get_stored() {
0073         assert(is_initialized_);
0074         PYBIND11_WARNING_PUSH
0075 #if !defined(__clang__) && defined(__GNUC__) && __GNUC__ < 5
0076         // Needed for gcc 4.8.5
0077         PYBIND11_WARNING_DISABLE_GCC("-Wstrict-aliasing")
0078 #endif
0079         return *reinterpret_cast<T *>(storage_);
0080         PYBIND11_WARNING_POP
0081     }
0082 
0083     constexpr gil_safe_call_once_and_store() = default;
0084     PYBIND11_DTOR_CONSTEXPR ~gil_safe_call_once_and_store() = default;
0085 
0086 private:
0087     alignas(T) char storage_[sizeof(T)] = {};
0088     std::once_flag once_flag_ = {};
0089 #ifdef Py_GIL_DISABLED
0090     std::atomic_bool
0091 #else
0092     bool
0093 #endif
0094         is_initialized_{false};
0095     // The `is_initialized_`-`storage_` pair is very similar to `std::optional`,
0096     // but the latter does not have the triviality properties of former,
0097     // therefore `std::optional` is not a viable alternative here.
0098 };
0099 
0100 PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)