mirror of
https://github.com/pybind/pybind11.git
synced 2026-03-14 20:27:47 +00:00
Fix thread-safety in get_local_type_info() (#5856)
Fixes potential thread-safety issues if types are concurrently registered while `get_local_type_info()` is called in free threaded Python. Use the `internals` mutex to also protect `local_internals`. This keeps the locking strategy simpler, and we already follow this pattern in some places, such as `pybind11_meta_dealloc`.
This commit is contained in:
@@ -91,6 +91,7 @@ static PyType_Spec function_record_PyType_Spec
|
||||
function_record_PyType_Slots};
|
||||
|
||||
inline PyTypeObject *get_function_record_PyTypeObject() {
|
||||
PYBIND11_LOCK_INTERNALS(get_internals());
|
||||
PyTypeObject *&py_type_obj = detail::get_local_internals().function_record_py_type;
|
||||
if (!py_type_obj) {
|
||||
PyObject *py_obj = PyType_FromSpec(&function_record_PyType_Spec);
|
||||
|
||||
@@ -205,7 +205,7 @@ PYBIND11_NOINLINE detail::type_info *get_type_info(PyTypeObject *type) {
|
||||
return bases.front();
|
||||
}
|
||||
|
||||
inline detail::type_info *get_local_type_info(const std::type_info &tp) {
|
||||
inline detail::type_info *get_local_type_info_lock_held(const std::type_info &tp) {
|
||||
const auto &locals = get_local_internals().registered_types_cpp;
|
||||
auto it = locals.find(&tp);
|
||||
if (it != locals.end()) {
|
||||
@@ -214,48 +214,59 @@ inline detail::type_info *get_local_type_info(const std::type_info &tp) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline detail::type_info *get_global_type_info(const std::type_info &tp) {
|
||||
inline detail::type_info *get_local_type_info(const std::type_info &tp) {
|
||||
// NB: internals and local_internals share a single mutex
|
||||
PYBIND11_LOCK_INTERNALS(get_internals());
|
||||
return get_local_type_info_lock_held(tp);
|
||||
}
|
||||
|
||||
inline detail::type_info *get_global_type_info_lock_held(const std::type_info &tp) {
|
||||
// This is a two-level lookup. Hopefully we find the type info in
|
||||
// registered_types_cpp_fast, but if not we try
|
||||
// registered_types_cpp and fill registered_types_cpp_fast for
|
||||
// next time.
|
||||
return with_internals([&](internals &internals) {
|
||||
detail::type_info *type_info = nullptr;
|
||||
detail::type_info *type_info = nullptr;
|
||||
auto &internals = get_internals();
|
||||
#if PYBIND11_INTERNALS_VERSION >= 12
|
||||
auto &fast_types = internals.registered_types_cpp_fast;
|
||||
auto &fast_types = internals.registered_types_cpp_fast;
|
||||
#endif
|
||||
auto &types = internals.registered_types_cpp;
|
||||
auto &types = internals.registered_types_cpp;
|
||||
#if PYBIND11_INTERNALS_VERSION >= 12
|
||||
auto fast_it = fast_types.find(&tp);
|
||||
if (fast_it != fast_types.end()) {
|
||||
auto fast_it = fast_types.find(&tp);
|
||||
if (fast_it != fast_types.end()) {
|
||||
# ifndef NDEBUG
|
||||
auto types_it = types.find(std::type_index(tp));
|
||||
assert(types_it != types.end());
|
||||
assert(types_it->second == fast_it->second);
|
||||
auto types_it = types.find(std::type_index(tp));
|
||||
assert(types_it != types.end());
|
||||
assert(types_it->second == fast_it->second);
|
||||
# endif
|
||||
return fast_it->second;
|
||||
}
|
||||
return fast_it->second;
|
||||
}
|
||||
#endif // PYBIND11_INTERNALS_VERSION >= 12
|
||||
|
||||
auto it = types.find(std::type_index(tp));
|
||||
if (it != types.end()) {
|
||||
auto it = types.find(std::type_index(tp));
|
||||
if (it != types.end()) {
|
||||
#if PYBIND11_INTERNALS_VERSION >= 12
|
||||
fast_types.emplace(&tp, it->second);
|
||||
fast_types.emplace(&tp, it->second);
|
||||
#endif
|
||||
type_info = it->second;
|
||||
}
|
||||
return type_info;
|
||||
});
|
||||
type_info = it->second;
|
||||
}
|
||||
return type_info;
|
||||
}
|
||||
|
||||
inline detail::type_info *get_global_type_info(const std::type_info &tp) {
|
||||
PYBIND11_LOCK_INTERNALS(get_internals());
|
||||
return get_global_type_info_lock_held(tp);
|
||||
}
|
||||
|
||||
/// Return the type info for a given C++ type; on lookup failure can either throw or return
|
||||
/// nullptr.
|
||||
PYBIND11_NOINLINE detail::type_info *get_type_info(const std::type_info &tp,
|
||||
bool throw_if_missing = false) {
|
||||
if (auto *ltype = get_local_type_info(tp)) {
|
||||
PYBIND11_LOCK_INTERNALS(get_internals());
|
||||
if (auto *ltype = get_local_type_info_lock_held(tp)) {
|
||||
return ltype;
|
||||
}
|
||||
if (auto *gtype = get_global_type_info(tp)) {
|
||||
if (auto *gtype = get_global_type_info_lock_held(tp)) {
|
||||
return gtype;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user