-
Notifications
You must be signed in to change notification settings - Fork 2.2k
feat: add pybind11/gil_safe_call_once.h (to fix deadlocks in pybind11/numpy.h) #4877
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 9 commits
38317c3
e7b8c4f
74ac0d9
109a165
88cec11
1ce2715
e7be9c2
f07b28b
36be645
7bc16a6
78f4e93
6d9441d
a864f21
6689b06
d965f29
398a42c
ab2cf8e
6d5bdd8
4c5dd1b
8633c5b
82f3efc
3ebd139
c33712d
dcf2b92
4557dce
704fe13
b2f87a8
fad1017
8453302
66bbc67
7cd9390
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,6 +14,7 @@ | |
|
||
#include <algorithm> | ||
#include <array> | ||
#include <cassert> | ||
#include <cstdint> | ||
#include <cstdlib> | ||
#include <cstring> | ||
|
@@ -42,6 +43,46 @@ 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 (reinterpret_cast<T *>(value_storage_)) T(std::move(value)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The inner cast is bogus. Placement-new takes a
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will be commit a864f21 (see below). Sorry I missed this before triggering the currently running GHA. I'll work on moving things to the right places before running GHA again.
|
||
initialized_ = true; | ||
} | ||
} | ||
PYBIND11_WARNING_PUSH | ||
#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ < 5 | ||
// Needed for gcc 4.8.5 | ||
PYBIND11_WARNING_DISABLE_CLANG("-Wstrict-aliasing") | ||
#endif | ||
return *reinterpret_cast<T *>(value_storage_); | ||
PYBIND11_WARNING_POP | ||
} | ||
|
||
constexpr LazyInitializeAtLeastOnceDestroyNever() = default; | ||
#if __cplusplus >= 202002L | ||
constexpr | ||
#endif // C++20 | ||
~LazyInitializeAtLeastOnceDestroyNever() | ||
= default; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can this go on a single line? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Only with
unfortunately. I feel it's better to just let clang-format do what it wants, but let me know if you prefer the override. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh OK, sure, never mind! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I found a nicer solution: |
||
|
||
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"); | ||
|
@@ -206,8 +247,8 @@ struct npy_api { | |
}; | ||
|
||
static npy_api &get() { | ||
static npy_api api = lookup(); | ||
return api; | ||
static LazyInitializeAtLeastOnceDestroyNever<npy_api> api_init; | ||
return api_init.Get(lookup); | ||
} | ||
|
||
bool PyArray_Check_(PyObject *obj) const { | ||
|
@@ -643,10 +684,11 @@ class dtype : public object { | |
char flags() const { return detail::array_descriptor_proxy(m_ptr)->flags; } | ||
|
||
private: | ||
static object _dtype_from_pep3118() { | ||
module_ m = detail::import_numpy_core_submodule("_internal"); | ||
static PyObject *obj = m.attr("_dtype_from_pep3118").cast<object>().release().ptr(); | ||
return reinterpret_borrow<object>(obj); | ||
static object &_dtype_from_pep3118() { | ||
static detail::LazyInitializeAtLeastOnceDestroyNever<object> imported_obj; | ||
return imported_obj.Get([]() { | ||
return detail::import_numpy_core_submodule("_internal").attr("_dtype_from_pep3118"); | ||
}); | ||
} | ||
|
||
dtype strip_padding(ssize_t itemsize) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Document that you expect the GIL to be held, or in any case that calls are serialized. (Otherwise the mutating accesses would cause races.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done: 1ce2715