| // Copyright (c) 2023 The pybind Community. |
| |
| #pragma once |
| |
| #include "detail/common.h" |
| #include "detail/internals.h" |
| #include "gil.h" |
| |
| #include <cassert> |
| #include <mutex> |
| |
| #if defined(Py_GIL_DISABLED) || defined(PYBIND11_HAS_SUBINTERPRETER_SUPPORT) |
| # include <atomic> |
| #endif |
| #ifdef PYBIND11_HAS_SUBINTERPRETER_SUPPORT |
| # include <cstdint> |
| # include <memory> |
| # include <string> |
| #endif |
| |
| PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) |
| |
| PYBIND11_NAMESPACE_BEGIN(detail) |
| #if defined(Py_GIL_DISABLED) || defined(PYBIND11_HAS_SUBINTERPRETER_SUPPORT) |
| using atomic_bool = std::atomic_bool; |
| #else |
| using atomic_bool = bool; |
| #endif |
| PYBIND11_NAMESPACE_END(detail) |
| |
| // Use the `gil_safe_call_once_and_store` 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 (because of missing lock ordering). |
| // |
| // The following alternative avoids both problems: |
| // |
| // PYBIND11_CONSTINIT static py::gil_safe_call_once_and_store<py::object> storage; |
| // auto &imported_obj = storage // Do NOT make this `static`! |
| // .call_once_and_store_result([]() { |
| // return py::module_::import("module_name"); |
| // }) |
| // .get_stored(); |
| // |
| // The parameter of `call_once_and_store_result()` must be callable. It can make |
| // CPython API calls, and in particular, it can temporarily release the GIL. |
| // |
| // `T` can be any C++ type, it does not have to involve CPython API types. |
| // |
| // The behavior with regard to signals, e.g. `SIGINT` (`KeyboardInterrupt`), |
| // is not ideal. If the main thread is the one to actually run the `Callable`, |
| // then a `KeyboardInterrupt` will interrupt it if it is running normal Python |
| // code. The situation is different if a non-main thread runs the |
| // `Callable`, and then the main thread starts waiting for it to complete: |
| // a `KeyboardInterrupt` will not interrupt the non-main thread, but it will |
| // get processed only when it is the main thread's turn again and it is running |
| // normal Python code. However, this will be unnoticeable for quick call-once |
| // functions, which is usually the case. |
| // |
| // For in-depth background, see docs/advanced/deadlock.md |
| #ifndef PYBIND11_HAS_SUBINTERPRETER_SUPPORT |
| // Subinterpreter support is disabled. |
| // In this case, we can store the result globally, because there is only a single interpreter. |
| // |
| // The life span of the stored result is the entire process lifetime. It is leaked on process |
| // termination to avoid destructor calls after the Python interpreter was finalized. |
| template <typename T> |
| class gil_safe_call_once_and_store { |
| public: |
| // PRECONDITION: The GIL must be held when `call_once_and_store_result()` is called. |
| // |
| // NOTE: The second parameter (finalize callback) is intentionally unused when subinterpreter |
| // support is disabled. In that case, storage is process-global and intentionally leaked to |
| // avoid calling destructors after the Python interpreter has been finalized. |
| template <typename Callable> |
| gil_safe_call_once_and_store &call_once_and_store_result(Callable &&fn, |
| void (*)(T &) /*unused*/ = nullptr) { |
| if (!is_initialized_) { // This read is guarded by the GIL. |
| // Multiple threads may enter here, because the GIL is released in the next line and |
| // CPython API calls in the `fn()` call below may release and reacquire the GIL. |
| gil_scoped_release gil_rel; // Needed to establish lock ordering. |
| std::call_once(once_flag_, [&] { |
| // Only one thread will ever enter here. |
| gil_scoped_acquire gil_acq; |
| ::new (storage_) T(fn()); // fn may release, but will reacquire, the GIL. |
| is_initialized_ = true; // This write is guarded by the GIL. |
| }); |
| // All threads will observe `is_initialized_` as true here. |
| } |
| // Intentionally not returning `T &` to ensure the calling code is self-documenting. |
| return *this; |
| } |
| |
| // This must only be called after `call_once_and_store_result()` was called. |
| T &get_stored() { |
| assert(is_initialized_); |
| 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 *>(storage_); |
| PYBIND11_WARNING_POP |
| } |
| |
| constexpr gil_safe_call_once_and_store() = default; |
| // The instance is a global static, so its destructor runs when the process |
| // is terminating. Therefore, do nothing here because the Python interpreter |
| // may have been finalized already. |
| PYBIND11_DTOR_CONSTEXPR ~gil_safe_call_once_and_store() = default; |
| |
| // Disable copy and move operations. |
| gil_safe_call_once_and_store(const gil_safe_call_once_and_store &) = delete; |
| gil_safe_call_once_and_store(gil_safe_call_once_and_store &&) = delete; |
| gil_safe_call_once_and_store &operator=(const gil_safe_call_once_and_store &) = delete; |
| gil_safe_call_once_and_store &operator=(gil_safe_call_once_and_store &&) = delete; |
| |
| private: |
| // The global static storage (per-process) when subinterpreter support is disabled. |
| alignas(T) char storage_[sizeof(T)] = {}; |
| std::once_flag once_flag_; |
| |
| // The `is_initialized_`-`storage_` pair is very similar to `std::optional`, |
| // but the latter does not have the triviality properties of former, |
| // therefore `std::optional` is not a viable alternative here. |
| detail::atomic_bool is_initialized_{false}; |
| }; |
| #else |
| // Subinterpreter support is enabled. |
| // In this case, we should store the result per-interpreter instead of globally, because each |
| // subinterpreter has its own separate state. The cached result may not shareable across |
| // interpreters (e.g., imported modules and their members). |
| |
| PYBIND11_NAMESPACE_BEGIN(detail) |
| |
| template <typename T> |
| struct call_once_storage { |
| alignas(T) char storage[sizeof(T)] = {}; |
| std::once_flag once_flag; |
| void (*finalize)(T &) = nullptr; |
| std::atomic_bool is_initialized{false}; |
| |
| call_once_storage() = default; |
| ~call_once_storage() { |
| if (is_initialized) { |
| if (finalize != nullptr) { |
| finalize(*reinterpret_cast<T *>(storage)); |
| } else { |
| reinterpret_cast<T *>(storage)->~T(); |
| } |
| } |
| } |
| call_once_storage(const call_once_storage &) = delete; |
| call_once_storage(call_once_storage &&) = delete; |
| call_once_storage &operator=(const call_once_storage &) = delete; |
| call_once_storage &operator=(call_once_storage &&) = delete; |
| }; |
| |
| PYBIND11_NAMESPACE_END(detail) |
| |
| // Prefix for storage keys in the interpreter state dict. |
| # define PYBIND11_CALL_ONCE_STORAGE_KEY_PREFIX PYBIND11_INTERNALS_ID "_call_once_storage__" |
| |
| // The life span of the stored result is the entire interpreter lifetime. An additional |
| // `finalize_fn` can be provided to clean up the stored result when the interpreter is destroyed. |
| template <typename T> |
| class gil_safe_call_once_and_store { |
| public: |
| // PRECONDITION: The GIL must be held when `call_once_and_store_result()` is called. |
| template <typename Callable> |
| gil_safe_call_once_and_store &call_once_and_store_result(Callable &&fn, |
| void (*finalize_fn)(T &) = nullptr) { |
| if (!is_last_storage_valid()) { |
| // Multiple threads may enter here, because the GIL is released in the next line and |
| // CPython API calls in the `fn()` call below may release and reacquire the GIL. |
| gil_scoped_release gil_rel; // Needed to establish lock ordering. |
| // There can be multiple threads going through here. |
| storage_type *value = nullptr; |
| { |
| gil_scoped_acquire gil_acq; // Restore lock ordering. |
| // This function is thread-safe under free-threading. |
| value = get_or_create_storage_in_state_dict(); |
| } |
| assert(value != nullptr); |
| std::call_once(value->once_flag, [&] { |
| // Only one thread will ever enter here. |
| gil_scoped_acquire gil_acq; |
| // fn may release, but will reacquire, the GIL. |
| ::new (value->storage) T(fn()); |
| value->finalize = finalize_fn; |
| value->is_initialized = true; |
| // Publish the cached pointer before setting the validity flag, so that any reader |
| // which observes the flag as true is guaranteed to also observe this pointer. |
| last_storage_ptr_ = reinterpret_cast<T *>(value->storage); |
| is_initialized_by_at_least_one_interpreter_ = true; |
| }); |
| // All threads will observe `is_initialized_by_at_least_one_interpreter_` as true here. |
| } |
| // Intentionally not returning `T &` to ensure the calling code is self-documenting. |
| return *this; |
| } |
| |
| // This must only be called after `call_once_and_store_result()` was called. |
| T &get_stored() { |
| T *result = nullptr; |
| // Check the validity flag *before* loading the cached pointer: the writer publishes the |
| // pointer before setting the flag, so observing the flag as true guarantees the load below |
| // sees the published pointer. |
| if (is_last_storage_valid()) { |
| result = last_storage_ptr_; |
| } else { |
| gil_scoped_acquire gil_acq; |
| auto *value = get_or_create_storage_in_state_dict(); |
| result = reinterpret_cast<T *>(value->storage); |
| last_storage_ptr_ = result; |
| } |
| assert(result != nullptr); |
| return *result; |
| } |
| |
| constexpr gil_safe_call_once_and_store() = default; |
| // The instance is a global static, so its destructor runs when the process |
| // is terminating. Therefore, do nothing here because the Python interpreter |
| // may have been finalized already. |
| PYBIND11_DTOR_CONSTEXPR ~gil_safe_call_once_and_store() = default; |
| |
| // Disable copy and move operations because the memory address is used as key. |
| gil_safe_call_once_and_store(const gil_safe_call_once_and_store &) = delete; |
| gil_safe_call_once_and_store(gil_safe_call_once_and_store &&) = delete; |
| gil_safe_call_once_and_store &operator=(const gil_safe_call_once_and_store &) = delete; |
| gil_safe_call_once_and_store &operator=(gil_safe_call_once_and_store &&) = delete; |
| |
| private: |
| using storage_type = detail::call_once_storage<T>; |
| |
| // Indicator of fast path for single-interpreter case. |
| bool is_last_storage_valid() const { |
| return is_initialized_by_at_least_one_interpreter_ |
| && !detail::has_seen_non_main_interpreter(); |
| } |
| |
| // Get the unique key for this storage instance in the interpreter's state dict. |
| // The return type should not be `py::str` because PyObject is interpreter-dependent. |
| std::string get_storage_key() const { |
| // The instance is expected to be global static, so using its address as unique identifier. |
| // The typical usage is like: |
| // |
| // PYBIND11_CONSTINIT static gil_safe_call_once_and_store<T> storage; |
| // |
| return PYBIND11_CALL_ONCE_STORAGE_KEY_PREFIX |
| + std::to_string(reinterpret_cast<std::uintptr_t>(this)); |
| } |
| |
| // Get or create per-storage capsule in the current interpreter's state dict. |
| // The storage is interpreter-dependent and will not be shared across interpreters. |
| storage_type *get_or_create_storage_in_state_dict() { |
| return detail::atomic_get_or_create_in_state_dict<storage_type>(get_storage_key().c_str()) |
| .first; |
| } |
| |
| // No storage needed when subinterpreter support is enabled. |
| // The actual storage is stored in the per-interpreter state dict via |
| // `get_or_create_storage_in_state_dict()`. |
| |
| // Fast local cache to avoid repeated lookups when there are no multiple interpreters. |
| // This is only valid if there is a single interpreter. Otherwise, it is not used. |
| // It is atomic because under free-threading (`Py_GIL_DISABLED`) the GIL provides no mutual |
| // exclusion, so this pointer may be read and written concurrently by multiple threads. |
| // WARNING: We cannot use thread local cache similar to `internals_pp_manager::internals_p_tls` |
| // because the thread local storage cannot be explicitly invalidated when interpreters |
| // are destroyed (unlike `internals_pp_manager` which has explicit hooks for that). |
| std::atomic<T *> last_storage_ptr_{nullptr}; |
| // This flag is true if the value has been initialized by any interpreter (may not be the |
| // current one). |
| detail::atomic_bool is_initialized_by_at_least_one_interpreter_{false}; |
| }; |
| #endif |
| |
| PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) |