fix: make gil_safe_call_once thread-safe in free-threaded CPython (#5246)

* fix: Make gil_safe_call_once thread-safe in free-threaded CPython

The "is_initialized_" flags is not protected by the GIL in free-threaded
Python, so it needs to be an atomic field.

Fixes #5245

* style: pre-commit fixes

* Apply changes from code review

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
Sam Gross 2024-07-16 14:06:54 -04:00 committed by GitHub
parent ccefee4c31
commit 43de8014f9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -8,6 +8,10 @@
#include <cassert> #include <cassert>
#include <mutex> #include <mutex>
#ifdef Py_GIL_DISABLED
# include <atomic>
#endif
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
// Use the `gil_safe_call_once_and_store` class below instead of the naive // Use the `gil_safe_call_once_and_store` class below instead of the naive
@ -82,7 +86,12 @@ public:
private: private:
alignas(T) char storage_[sizeof(T)] = {}; alignas(T) char storage_[sizeof(T)] = {};
std::once_flag once_flag_ = {}; std::once_flag once_flag_ = {};
bool is_initialized_ = false; #ifdef Py_GIL_DISABLED
std::atomic_bool
#else
bool
#endif
is_initialized_{false};
// The `is_initialized_`-`storage_` pair is very similar to `std::optional`, // The `is_initialized_`-`storage_` pair is very similar to `std::optional`,
// but the latter does not have the triviality properties of former, // but the latter does not have the triviality properties of former,
// therefore `std::optional` is not a viable alternative here. // therefore `std::optional` is not a viable alternative here.