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:
Sam Gross
2025-10-12 17:37:48 -04:00
committed by GitHub
parent aa4259b4f8
commit 9f75202191
2 changed files with 34 additions and 22 deletions

View File

@@ -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);

View File

@@ -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;
}