diff --git a/CMakeLists.txt b/CMakeLists.txt index f53a646b67..46f0a1f11d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -194,6 +194,7 @@ set(PYBIND11_HEADERS include/pybind11/detail/class.h include/pybind11/detail/common-inl.h include/pybind11/detail/common.h + include/pybind11/detail/cpp_conduit-inl.h include/pybind11/detail/cpp_conduit.h include/pybind11/detail/descr.h include/pybind11/detail/dynamic_raw_ptr_cast_if_possible.h @@ -210,9 +211,11 @@ set(PYBIND11_HEADERS include/pybind11/detail/struct_smart_holder.h include/pybind11/detail/type_caster_base-inl.h include/pybind11/detail/type_caster_base.h + include/pybind11/detail/typeid-inl.h include/pybind11/detail/typeid.h include/pybind11/detail/using_smart_holder.h include/pybind11/detail/value_and_holder.h + include/pybind11/attr-inl.h include/pybind11/attr.h include/pybind11/buffer_info.h include/pybind11/cast.h @@ -230,6 +233,7 @@ set(PYBIND11_HEADERS include/pybind11/eigen/tensor.h include/pybind11/embed.h include/pybind11/eval.h + include/pybind11/gil-inl.h include/pybind11/gil.h include/pybind11/gil_safe_call_once.h include/pybind11/gil_simple.h diff --git a/include/pybind11/attr-inl.h b/include/pybind11/attr-inl.h new file mode 100644 index 0000000000..b3716bc3cb --- /dev/null +++ b/include/pybind11/attr-inl.h @@ -0,0 +1,64 @@ +/* + pybind11/attr-inl.h: Out-of-line definitions for attr.h + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +// Every function defined here must start with PYBIND11_INLINE (or +// PYBIND11_NOINLINE_ATTR PYBIND11_INLINE). In the default header-only mode this file is +// included at the bottom of attr.h; when PYBIND11_PRECOMPILED is defined it is only +// compiled into the pybind11 static library (see src/). + +#pragma once + +#include "attr.h" + +#include + +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN(detail) + +PYBIND11_NOINLINE_ATTR PYBIND11_INLINE type_record::type_record() + : multiple_inheritance(false), dynamic_attr(false), buffer_protocol(false), + module_local(false), is_final(false), release_gil_before_calling_cpp_dtor(false) {} + +PYBIND11_NOINLINE_ATTR PYBIND11_INLINE void type_record::add_base(const std::type_info &base, + void *(*caster)(void *) ) { + auto *base_info = detail::get_type_info(base, false); + if (!base_info) { + std::string tname(base.name()); + detail::clean_type_id(tname); + pybind11_fail("generic_type: type \"" + std::string(name) + + "\" referenced unknown base type \"" + tname + "\""); + } + + // SMART_HOLDER_BAKEIN_FOLLOW_ON: Refine holder compatibility checks. + bool this_has_unique_ptr_holder = (holder_enum_v == holder_enum_t::std_unique_ptr); + bool base_has_unique_ptr_holder = (base_info->holder_enum_v == holder_enum_t::std_unique_ptr); + if (this_has_unique_ptr_holder != base_has_unique_ptr_holder) { + std::string tname(base.name()); + detail::clean_type_id(tname); + pybind11_fail("generic_type: type \"" + std::string(name) + "\" " + + (this_has_unique_ptr_holder ? "does not have" : "has") + + " a non-default holder type while its base \"" + tname + "\" " + + (base_has_unique_ptr_holder ? "does not" : "does")); + } + + bases.append(reinterpret_cast(base_info->type)); + +#ifdef PYBIND11_BACKWARD_COMPATIBILITY_TP_DICTOFFSET + dynamic_attr |= base_info->type->tp_dictoffset != 0; +#else + dynamic_attr |= (PyType_GetFlags(base_info->type) & Py_TPFLAGS_MANAGED_DICT) != 0; +#endif + + if (caster) { + base_info->implicit_casts.emplace_back(type, caster); + } +} + +PYBIND11_NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/include/pybind11/attr.h b/include/pybind11/attr.h index d337595a0b..2a948797d8 100644 --- a/include/pybind11/attr.h +++ b/include/pybind11/attr.h @@ -282,9 +282,7 @@ struct function_record { /// Special data structure which (temporarily) holds metadata about a bound class struct type_record { - PYBIND11_NOINLINE type_record() - : multiple_inheritance(false), dynamic_attr(false), buffer_protocol(false), - module_local(false), is_final(false), release_gil_before_calling_cpp_dtor(false) {} + type_record(); /// Handle to the parent scope handle scope; @@ -351,40 +349,7 @@ struct type_record { holder_enum_t holder_enum_v = holder_enum_t::undefined; - PYBIND11_NOINLINE void add_base(const std::type_info &base, void *(*caster)(void *) ) { - auto *base_info = detail::get_type_info(base, false); - if (!base_info) { - std::string tname(base.name()); - detail::clean_type_id(tname); - pybind11_fail("generic_type: type \"" + std::string(name) - + "\" referenced unknown base type \"" + tname + "\""); - } - - // SMART_HOLDER_BAKEIN_FOLLOW_ON: Refine holder compatibility checks. - bool this_has_unique_ptr_holder = (holder_enum_v == holder_enum_t::std_unique_ptr); - bool base_has_unique_ptr_holder - = (base_info->holder_enum_v == holder_enum_t::std_unique_ptr); - if (this_has_unique_ptr_holder != base_has_unique_ptr_holder) { - std::string tname(base.name()); - detail::clean_type_id(tname); - pybind11_fail("generic_type: type \"" + std::string(name) + "\" " - + (this_has_unique_ptr_holder ? "does not have" : "has") - + " a non-default holder type while its base \"" + tname + "\" " - + (base_has_unique_ptr_holder ? "does not" : "does")); - } - - bases.append(reinterpret_cast(base_info->type)); - -#ifdef PYBIND11_BACKWARD_COMPATIBILITY_TP_DICTOFFSET - dynamic_attr |= base_info->type->tp_dictoffset != 0; -#else - dynamic_attr |= (PyType_GetFlags(base_info->type) & Py_TPFLAGS_MANAGED_DICT) != 0; -#endif - - if (caster) { - base_info->implicit_casts.emplace_back(type, caster); - } - } + void add_base(const std::type_info &base, void *(*caster)(void *) ); }; inline function_call::function_call(const function_record &f, handle p) : func(f), parent(p) { @@ -728,3 +693,7 @@ constexpr bool expected_num_args(size_t nargs, bool has_args, bool has_kwargs) { PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) + +#ifndef PYBIND11_PRECOMPILED +# include "attr-inl.h" // IWYU pragma: export +#endif diff --git a/include/pybind11/detail/cpp_conduit-inl.h b/include/pybind11/detail/cpp_conduit-inl.h new file mode 100644 index 0000000000..60d0ef215e --- /dev/null +++ b/include/pybind11/detail/cpp_conduit-inl.h @@ -0,0 +1,79 @@ +/* + pybind11/detail/cpp_conduit-inl.h: Out-of-line definitions for cpp_conduit.h + + Copyright (c) 2024 The pybind Community. +*/ + +// Every function defined here must start with PYBIND11_INLINE (or +// PYBIND11_NOINLINE_ATTR PYBIND11_INLINE). In the default header-only mode this file is +// included at the bottom of cpp_conduit.h; when PYBIND11_PRECOMPILED is defined it is only +// compiled into the pybind11 static library (see src/). + +#pragma once + +#include "../conduit/pybind11_platform_abi_id.h" +#include "cpp_conduit.h" + +#include + +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN(detail) + +PYBIND11_INLINE bool type_is_managed_by_our_internals(PyTypeObject *type_obj) { +#if defined(PYPY_VERSION) + auto &internals = get_internals(); + return bool(internals.registered_types_py.find(type_obj) + != internals.registered_types_py.end()); +#else + return (type_obj->tp_new == pybind11_object_new); +#endif +} + +PYBIND11_INLINE bool is_instance_method_of_type(PyTypeObject *type_obj, PyObject *attr_name) { + PyObject *descr = _PyType_Lookup(type_obj, attr_name); + return ((descr != nullptr) && PyInstanceMethod_Check(descr)); +} + +PYBIND11_INLINE object try_get_cpp_conduit_method(PyObject *obj) { + if (PyType_Check(obj)) { + return object(); + } + PyTypeObject *type_obj = Py_TYPE(obj); + str attr_name("_pybind11_conduit_v1_"); + bool assumed_to_be_callable = false; + if (type_is_managed_by_our_internals(type_obj)) { + if (!is_instance_method_of_type(type_obj, attr_name.ptr())) { + return object(); + } + assumed_to_be_callable = true; + } + PyObject *method = PyObject_GetAttr(obj, attr_name.ptr()); + if (method == nullptr) { + PyErr_Clear(); + return object(); + } + if (!assumed_to_be_callable && PyCallable_Check(method) == 0) { + Py_DECREF(method); + return object(); + } + return reinterpret_steal(method); +} + +PYBIND11_INLINE void * +try_raw_pointer_ephemeral_from_cpp_conduit(handle src, const std::type_info *cpp_type_info) { + object method = try_get_cpp_conduit_method(src.ptr()); + if (method) { + capsule cpp_type_info_capsule(const_cast(static_cast(cpp_type_info)), + typeid(std::type_info).name()); + object cpp_conduit = method(bytes(PYBIND11_PLATFORM_ABI_ID), + cpp_type_info_capsule, + bytes("raw_pointer_ephemeral")); + if (isinstance(cpp_conduit)) { + return reinterpret_borrow(cpp_conduit).get_pointer(); + } + } + return nullptr; +} + +PYBIND11_NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/include/pybind11/detail/cpp_conduit.h b/include/pybind11/detail/cpp_conduit.h index c9ce32756c..9560270782 100644 --- a/include/pybind11/detail/cpp_conduit.h +++ b/include/pybind11/detail/cpp_conduit.h @@ -15,61 +15,17 @@ PYBIND11_NAMESPACE_BEGIN(detail) // Forward declaration needed here: Refactoring opportunity. extern "C" PyObject *pybind11_object_new(PyTypeObject *type, PyObject *, PyObject *); -inline bool type_is_managed_by_our_internals(PyTypeObject *type_obj) { -#if defined(PYPY_VERSION) - auto &internals = get_internals(); - return bool(internals.registered_types_py.find(type_obj) - != internals.registered_types_py.end()); -#else - return (type_obj->tp_new == pybind11_object_new); -#endif -} +bool type_is_managed_by_our_internals(PyTypeObject *type_obj); -inline bool is_instance_method_of_type(PyTypeObject *type_obj, PyObject *attr_name) { - PyObject *descr = _PyType_Lookup(type_obj, attr_name); - return ((descr != nullptr) && PyInstanceMethod_Check(descr)); -} +bool is_instance_method_of_type(PyTypeObject *type_obj, PyObject *attr_name); -inline object try_get_cpp_conduit_method(PyObject *obj) { - if (PyType_Check(obj)) { - return object(); - } - PyTypeObject *type_obj = Py_TYPE(obj); - str attr_name("_pybind11_conduit_v1_"); - bool assumed_to_be_callable = false; - if (type_is_managed_by_our_internals(type_obj)) { - if (!is_instance_method_of_type(type_obj, attr_name.ptr())) { - return object(); - } - assumed_to_be_callable = true; - } - PyObject *method = PyObject_GetAttr(obj, attr_name.ptr()); - if (method == nullptr) { - PyErr_Clear(); - return object(); - } - if (!assumed_to_be_callable && PyCallable_Check(method) == 0) { - Py_DECREF(method); - return object(); - } - return reinterpret_steal(method); -} +object try_get_cpp_conduit_method(PyObject *obj); -inline void *try_raw_pointer_ephemeral_from_cpp_conduit(handle src, - const std::type_info *cpp_type_info) { - object method = try_get_cpp_conduit_method(src.ptr()); - if (method) { - capsule cpp_type_info_capsule(const_cast(static_cast(cpp_type_info)), - typeid(std::type_info).name()); - object cpp_conduit = method(bytes(PYBIND11_PLATFORM_ABI_ID), - cpp_type_info_capsule, - bytes("raw_pointer_ephemeral")); - if (isinstance(cpp_conduit)) { - return reinterpret_borrow(cpp_conduit).get_pointer(); - } - } - return nullptr; -} +void *try_raw_pointer_ephemeral_from_cpp_conduit(handle src, const std::type_info *cpp_type_info); PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) + +#ifndef PYBIND11_PRECOMPILED +# include "cpp_conduit-inl.h" // IWYU pragma: export +#endif diff --git a/include/pybind11/detail/internals-inl.h b/include/pybind11/detail/internals-inl.h index ea1998435a..a34e447a1b 100644 --- a/include/pybind11/detail/internals-inl.h +++ b/include/pybind11/detail/internals-inl.h @@ -256,4 +256,19 @@ PYBIND11_INLINE void PYBIND11_PRECOMPILED_CONFIG_CHECK() {} #endif PYBIND11_NAMESPACE_END(detail) + +PYBIND11_NOINLINE_ATTR PYBIND11_INLINE void *get_shared_data(const std::string &name) { + return detail::with_internals([&](detail::internals &internals) { + auto it = internals.shared_data.find(name); + return it != internals.shared_data.end() ? it->second : nullptr; + }); +} + +PYBIND11_NOINLINE_ATTR PYBIND11_INLINE void *set_shared_data(const std::string &name, void *data) { + return detail::with_internals([&](detail::internals &internals) { + internals.shared_data[name] = data; + return data; + }); +} + PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/include/pybind11/detail/internals.h b/include/pybind11/detail/internals.h index 164da39b5c..cb0714ed0a 100644 --- a/include/pybind11/detail/internals.h +++ b/include/pybind11/detail/internals.h @@ -917,20 +917,10 @@ PYBIND11_NAMESPACE_END(detail) /// Returns a named pointer that is shared among all extension modules (using the same /// pybind11 version) running in the current interpreter. Names starting with underscores /// are reserved for internal usage. Returns `nullptr` if no matching entry was found. -PYBIND11_NOINLINE void *get_shared_data(const std::string &name) { - return detail::with_internals([&](detail::internals &internals) { - auto it = internals.shared_data.find(name); - return it != internals.shared_data.end() ? it->second : nullptr; - }); -} +void *get_shared_data(const std::string &name); /// Set the shared data that can be later recovered by `get_shared_data()`. -PYBIND11_NOINLINE void *set_shared_data(const std::string &name, void *data) { - return detail::with_internals([&](detail::internals &internals) { - internals.shared_data[name] = data; - return data; - }); -} +void *set_shared_data(const std::string &name, void *data); /// Returns a typed reference to a shared data entry (by using `get_shared_data()`) if /// such entry exists. Otherwise, a new object of default-constructible type `T` is diff --git a/include/pybind11/detail/type_caster_base-inl.h b/include/pybind11/detail/type_caster_base-inl.h index f94f39dc62..892b2c55b9 100644 --- a/include/pybind11/detail/type_caster_base-inl.h +++ b/include/pybind11/detail/type_caster_base-inl.h @@ -550,5 +550,50 @@ type_caster_generic::cast(const cast_sources &srcs, return inst.release(); } +PYBIND11_INLINE void smart_holder_type_caster_support::value_and_holder_helper:: + throw_if_uninitialized_or_disowned_holder(const char *typeid_name) const { + static const std::string missing_value_msg = "Missing value for wrapped C++ type `"; + if (!holder().is_populated) { + throw value_error(missing_value_msg + clean_type_id(typeid_name) + + "`: Python instance is uninitialized."); + } + if (!holder().has_pointee()) { + throw value_error(missing_value_msg + clean_type_id(typeid_name) + + "`: Python instance was disowned."); + } +} + +PYBIND11_NOINLINE_ATTR PYBIND11_INLINE void *type_caster_generic::local_load(PyObject *src, + const type_info *ti) { + auto caster = type_caster_generic(ti); + if (caster.load(src, false)) { + return caster.value; + } + return nullptr; +} + +PYBIND11_NOINLINE_ATTR PYBIND11_INLINE bool +type_caster_generic::try_load_foreign_module_local(handle src) { + constexpr auto *local_key = PYBIND11_MODULE_LOCAL_ID; + const auto pytype = type::handle_of(src); + if (!hasattr(pytype, local_key)) { + return false; + } + + type_info *foreign_typeinfo = reinterpret_borrow(getattr(pytype, local_key)); + // Only consider this foreign loader if actually foreign and is a loader of the correct cpp + // type + if (foreign_typeinfo->module_local_load == &local_load + || (cpptype && !same_type(*cpptype, *foreign_typeinfo->cpptype))) { + return false; + } + + if (auto *result = foreign_typeinfo->module_local_load(src.ptr(), foreign_typeinfo)) { + value = result; + return true; + } + return false; +} + PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/include/pybind11/detail/type_caster_base.h b/include/pybind11/detail/type_caster_base.h index 7d9c58d78c..7fdab68fcf 100644 --- a/include/pybind11/detail/type_caster_base.h +++ b/include/pybind11/detail/type_caster_base.h @@ -367,17 +367,7 @@ struct value_and_holder_helper { smart_holder &holder() const { return loaded_v_h.holder(); } - void throw_if_uninitialized_or_disowned_holder(const char *typeid_name) const { - static const std::string missing_value_msg = "Missing value for wrapped C++ type `"; - if (!holder().is_populated) { - throw value_error(missing_value_msg + clean_type_id(typeid_name) - + "`: Python instance is uninitialized."); - } - if (!holder().has_pointee()) { - throw value_error(missing_value_msg + clean_type_id(typeid_name) - + "`: Python instance was disowned."); - } - } + void throw_if_uninitialized_or_disowned_holder(const char *typeid_name) const; void throw_if_uninitialized_or_disowned_holder(const std::type_info &type_info) const { throw_if_uninitialized_or_disowned_holder(type_info.name()); @@ -826,37 +816,11 @@ class type_caster_generic { void check_holder_compat() {} bool set_foreign_holder(handle) { return true; } - PYBIND11_NOINLINE static void *local_load(PyObject *src, const type_info *ti) { - auto caster = type_caster_generic(ti); - if (caster.load(src, false)) { - return caster.value; - } - return nullptr; - } + static void *local_load(PyObject *src, const type_info *ti); /// Try to load with foreign typeinfo, if available. Used when there is no /// native typeinfo, or when the native one wasn't able to produce a value. - PYBIND11_NOINLINE bool try_load_foreign_module_local(handle src) { - constexpr auto *local_key = PYBIND11_MODULE_LOCAL_ID; - const auto pytype = type::handle_of(src); - if (!hasattr(pytype, local_key)) { - return false; - } - - type_info *foreign_typeinfo = reinterpret_borrow(getattr(pytype, local_key)); - // Only consider this foreign loader if actually foreign and is a loader of the correct cpp - // type - if (foreign_typeinfo->module_local_load == &local_load - || (cpptype && !same_type(*cpptype, *foreign_typeinfo->cpptype))) { - return false; - } - - if (auto *result = foreign_typeinfo->module_local_load(src.ptr(), foreign_typeinfo)) { - value = result; - return true; - } - return false; - } + bool try_load_foreign_module_local(handle src); // Implementation of `load`; this takes the type of `this` so that it can dispatch the relevant // bits of code between here and copyable_holder_caster where the two classes need different diff --git a/include/pybind11/detail/typeid-inl.h b/include/pybind11/detail/typeid-inl.h new file mode 100644 index 0000000000..37a6a6a42e --- /dev/null +++ b/include/pybind11/detail/typeid-inl.h @@ -0,0 +1,49 @@ +/* + pybind11/detail/typeid-inl.h: Out-of-line definitions for typeid.h + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +// Every function defined here must start with PYBIND11_INLINE (or +// PYBIND11_NOINLINE_ATTR PYBIND11_INLINE). In the default header-only mode this file is +// included at the bottom of typeid.h; when PYBIND11_PRECOMPILED is defined it is only +// compiled into the pybind11 static library (see src/). + +#pragma once + +#include "typeid.h" + +#include +#include +#include + +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN(detail) + +PYBIND11_NOINLINE_ATTR PYBIND11_INLINE void clean_type_id(std::string &name) { +#if defined(PYBIND11_HAS_CXXABI_H) + int status = 0; + std::unique_ptr res{ + abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), std::free}; + if (status == 0) { + name = res.get(); + } +#else + detail::erase_all(name, "class "); + detail::erase_all(name, "struct "); + detail::erase_all(name, "enum "); +#endif + detail::erase_all(name, "pybind11::"); +} + +PYBIND11_INLINE std::string clean_type_id(const char *typeid_name) { + std::string name(typeid_name); + detail::clean_type_id(name); + return name; +} + +PYBIND11_NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/include/pybind11/detail/typeid.h b/include/pybind11/detail/typeid.h index 4068dd29e4..c8160ad135 100644 --- a/include/pybind11/detail/typeid.h +++ b/include/pybind11/detail/typeid.h @@ -42,27 +42,9 @@ inline void erase_all(std::string &string, const std::string &search) { } } -PYBIND11_NOINLINE void clean_type_id(std::string &name) { -#if defined(PYBIND11_HAS_CXXABI_H) - int status = 0; - std::unique_ptr res{ - abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), std::free}; - if (status == 0) { - name = res.get(); - } -#else - detail::erase_all(name, "class "); - detail::erase_all(name, "struct "); - detail::erase_all(name, "enum "); -#endif - detail::erase_all(name, "pybind11::"); -} +void clean_type_id(std::string &name); -inline std::string clean_type_id(const char *typeid_name) { - std::string name(typeid_name); - detail::clean_type_id(name); - return name; -} +std::string clean_type_id(const char *typeid_name); PYBIND11_NAMESPACE_END(detail) @@ -73,3 +55,7 @@ std::string type_id() { } PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) + +#ifndef PYBIND11_PRECOMPILED +# include "typeid-inl.h" // IWYU pragma: export +#endif diff --git a/include/pybind11/gil-inl.h b/include/pybind11/gil-inl.h new file mode 100644 index 0000000000..2ac92875a2 --- /dev/null +++ b/include/pybind11/gil-inl.h @@ -0,0 +1,122 @@ +/* + pybind11/gil-inl.h: Out-of-line definitions for gil.h + + Copyright (c) 2016 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +// Every function defined here must start with PYBIND11_INLINE (or +// PYBIND11_NOINLINE_ATTR PYBIND11_INLINE). In the default header-only mode this file is +// included at the bottom of gil.h; when PYBIND11_PRECOMPILED is defined it is only +// compiled into the pybind11 static library (see src/). + +#pragma once + +#include "gil.h" + +#if !defined(PYBIND11_SIMPLE_GIL_MANAGEMENT) + +# include + +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +PYBIND11_NOINLINE_ATTR PYBIND11_INLINE gil_scoped_acquire::gil_scoped_acquire() { + auto &internals = detail::get_internals(); + tstate = internals.tstate.get(); + + if (!tstate) { + /* Check if the GIL was acquired using the PyGILState_* API instead (e.g. if + calling from a Python thread). Since we use a different key, this ensures + we don't create a new thread state and deadlock in PyEval_AcquireThread + below. Note we don't save this state with internals.tstate, since we don't + create it we would fail to clear it (its reference count should be > 0). */ + tstate = PyGILState_GetThisThreadState(); + } + + if (!tstate) { + tstate = PyThreadState_New(internals.istate); +# if defined(PYBIND11_DETAILED_ERROR_MESSAGES) + if (!tstate) { + pybind11_fail("scoped_acquire: could not create thread state!"); + } +# endif + tstate->gilstate_counter = 0; + internals.tstate = tstate; + } else { + release = detail::get_thread_state_unchecked() != tstate; + } + + if (release) { + PyEval_AcquireThread(tstate); + } + + inc_ref(); +} + +PYBIND11_NOINLINE_ATTR PYBIND11_INLINE void gil_scoped_acquire::dec_ref() { + --tstate->gilstate_counter; +# if defined(PYBIND11_DETAILED_ERROR_MESSAGES) + if (detail::get_thread_state_unchecked() != tstate) { + pybind11_fail("scoped_acquire::dec_ref(): thread state must be current!"); + } + if (tstate->gilstate_counter < 0) { + pybind11_fail("scoped_acquire::dec_ref(): reference count underflow!"); + } +# endif + if (tstate->gilstate_counter == 0) { +# if defined(PYBIND11_DETAILED_ERROR_MESSAGES) + if (!release) { + pybind11_fail("scoped_acquire::dec_ref(): internal error!"); + } +# endif + // Make sure that PyThreadState_Clear is not recursively called by finalizers. + // See issue #5827 + ++tstate->gilstate_counter; + PyThreadState_Clear(tstate); + --tstate->gilstate_counter; + if (active) { + PyThreadState_DeleteCurrent(); + } + detail::get_internals().tstate.reset(); + release = false; + } +} + +PYBIND11_NOINLINE_ATTR PYBIND11_INLINE gil_scoped_acquire::~gil_scoped_acquire() { + dec_ref(); + if (release) { + PyEval_SaveThread(); + } +} + +PYBIND11_INLINE gil_scoped_release::gil_scoped_release(bool disassoc) : disassoc(disassoc) { + assert(PyGILState_Check()); + // `get_internals()` must be called here unconditionally in order to initialize + // `internals.tstate` for subsequent `gil_scoped_acquire` calls. Otherwise, an + // initialization race could occur as multiple threads try `gil_scoped_acquire`. + auto &internals = detail::get_internals(); + // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) + tstate = PyEval_SaveThread(); + if (disassoc) { + internals.tstate.reset(); + } +} + +PYBIND11_INLINE gil_scoped_release::~gil_scoped_release() { + if (!tstate) { + return; + } + // `PyEval_RestoreThread()` should not be called if runtime is finalizing + if (active) { + PyEval_RestoreThread(tstate); + } + if (disassoc) { + detail::get_internals().tstate = tstate; + } +} + +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) + +#endif // !PYBIND11_SIMPLE_GIL_MANAGEMENT diff --git a/include/pybind11/gil.h b/include/pybind11/gil.h index e43d4cb2e9..121d1cc40c 100644 --- a/include/pybind11/gil.h +++ b/include/pybind11/gil.h @@ -28,8 +28,6 @@ PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) # include "detail/internals.h" -# include - PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(detail) @@ -68,72 +66,14 @@ PYBIND11_NAMESPACE_END(detail) class gil_scoped_acquire { public: - PYBIND11_NOINLINE gil_scoped_acquire() { - auto &internals = detail::get_internals(); - tstate = internals.tstate.get(); - - if (!tstate) { - /* Check if the GIL was acquired using the PyGILState_* API instead (e.g. if - calling from a Python thread). Since we use a different key, this ensures - we don't create a new thread state and deadlock in PyEval_AcquireThread - below. Note we don't save this state with internals.tstate, since we don't - create it we would fail to clear it (its reference count should be > 0). */ - tstate = PyGILState_GetThisThreadState(); - } - - if (!tstate) { - tstate = PyThreadState_New(internals.istate); -# if defined(PYBIND11_DETAILED_ERROR_MESSAGES) - if (!tstate) { - pybind11_fail("scoped_acquire: could not create thread state!"); - } -# endif - tstate->gilstate_counter = 0; - internals.tstate = tstate; - } else { - release = detail::get_thread_state_unchecked() != tstate; - } - - if (release) { - PyEval_AcquireThread(tstate); - } - - inc_ref(); - } + gil_scoped_acquire(); gil_scoped_acquire(const gil_scoped_acquire &) = delete; gil_scoped_acquire &operator=(const gil_scoped_acquire &) = delete; void inc_ref() { ++tstate->gilstate_counter; } - PYBIND11_NOINLINE void dec_ref() { - --tstate->gilstate_counter; -# if defined(PYBIND11_DETAILED_ERROR_MESSAGES) - if (detail::get_thread_state_unchecked() != tstate) { - pybind11_fail("scoped_acquire::dec_ref(): thread state must be current!"); - } - if (tstate->gilstate_counter < 0) { - pybind11_fail("scoped_acquire::dec_ref(): reference count underflow!"); - } -# endif - if (tstate->gilstate_counter == 0) { -# if defined(PYBIND11_DETAILED_ERROR_MESSAGES) - if (!release) { - pybind11_fail("scoped_acquire::dec_ref(): internal error!"); - } -# endif - // Make sure that PyThreadState_Clear is not recursively called by finalizers. - // See issue #5827 - ++tstate->gilstate_counter; - PyThreadState_Clear(tstate); - --tstate->gilstate_counter; - if (active) { - PyThreadState_DeleteCurrent(); - } - detail::get_internals().tstate.reset(); - release = false; - } - } + void dec_ref(); /// This method will disable the PyThreadState_DeleteCurrent call and the /// GIL won't be released. This method should be used if the interpreter @@ -142,12 +82,7 @@ class gil_scoped_acquire { /// protect subsequent code. PYBIND11_NOINLINE void disarm() { active = false; } - PYBIND11_NOINLINE ~gil_scoped_acquire() { - dec_ref(); - if (release) { - PyEval_SaveThread(); - } - } + ~gil_scoped_acquire(); private: PyThreadState *tstate = nullptr; @@ -158,18 +93,7 @@ class gil_scoped_acquire { class gil_scoped_release { public: // PRECONDITION: The GIL must be held when this constructor is called. - explicit gil_scoped_release(bool disassoc = false) : disassoc(disassoc) { - assert(PyGILState_Check()); - // `get_internals()` must be called here unconditionally in order to initialize - // `internals.tstate` for subsequent `gil_scoped_acquire` calls. Otherwise, an - // initialization race could occur as multiple threads try `gil_scoped_acquire`. - auto &internals = detail::get_internals(); - // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) - tstate = PyEval_SaveThread(); - if (disassoc) { - internals.tstate.reset(); - } - } + explicit gil_scoped_release(bool disassoc = false); gil_scoped_release(const gil_scoped_release &) = delete; gil_scoped_release &operator=(const gil_scoped_release &) = delete; @@ -181,18 +105,7 @@ class gil_scoped_release { /// protect subsequent code. PYBIND11_NOINLINE void disarm() { active = false; } - ~gil_scoped_release() { - if (!tstate) { - return; - } - // `PyEval_RestoreThread()` should not be called if runtime is finalizing - if (active) { - PyEval_RestoreThread(tstate); - } - if (disassoc) { - detail::get_internals().tstate = tstate; - } - } + ~gil_scoped_release(); private: PyThreadState *tstate; @@ -202,4 +115,8 @@ class gil_scoped_release { PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) +# ifndef PYBIND11_PRECOMPILED +# include "gil-inl.h" // IWYU pragma: export +# endif + #endif // !PYBIND11_SIMPLE_GIL_MANAGEMENT diff --git a/include/pybind11/pybind11-inl.h b/include/pybind11/pybind11-inl.h index f34c24bfd4..1a0989f202 100644 --- a/include/pybind11/pybind11-inl.h +++ b/include/pybind11/pybind11-inl.h @@ -408,6 +408,10 @@ PYBIND11_NOINLINE_ATTR PYBIND11_INLINE void print(const tuple &args, const dict } PYBIND11_NAMESPACE_END(detail) +PYBIND11_INLINE error_already_set::error_already_set() + : m_fetched_error{new detail::error_fetch_and_normalize("pybind11::error_already_set"), + m_fetched_error_deleter} {} + PYBIND11_INLINE void error_already_set::m_fetched_error_deleter(detail::error_fetch_and_normalize *raw_ptr) { gil_scoped_acquire gil; @@ -1489,4 +1493,98 @@ PYBIND11_NOINLINE_ATTR PYBIND11_INLINE void enum_base::export_values() { } PYBIND11_NAMESPACE_END(detail) +PYBIND11_INLINE module_ module_::def_submodule(const char *name, const char *doc) { + const char *this_name = PyModule_GetName(m_ptr); + if (this_name == nullptr) { + throw error_already_set(); + } + std::string full_name = std::string(this_name) + '.' + name; + handle submodule = PyImport_AddModule(full_name.c_str()); + if (!submodule) { + throw error_already_set(); + } + auto result = reinterpret_borrow(submodule); + if (doc && options::show_user_defined_docstrings()) { + result.attr("__doc__") = pybind11::str(doc); + } + +#if defined(GRAALVM_PYTHON) && (!defined(GRAALPY_VERSION_NUM) || GRAALPY_VERSION_NUM < 0x190000) + // GraalPy doesn't support PyModule_GetFilenameObject, + // so getting by attribute (see PR #5584) + handle this_module = m_ptr; + if (object this_file = getattr(this_module, "__file__", none())) { + result.attr("__file__") = this_file; + } +#else + handle this_file = PyModule_GetFilenameObject(m_ptr); + if (this_file) { + result.attr("__file__") = this_file; + } else if (PyErr_ExceptionMatches(PyExc_SystemError) != 0) { + PyErr_Clear(); + } else { + throw error_already_set(); + } +#endif + attr(name) = result; + return result; +} + +PYBIND11_INLINE module_ module_::import(const char *name) { + PyObject *obj = PyImport_ImportModule(name); + if (!obj) { + throw error_already_set(); + } + return reinterpret_steal(obj); +} + +PYBIND11_INLINE void module_::reload() { + PyObject *obj = PyImport_ReloadModule(ptr()); + if (!obj) { + throw error_already_set(); + } + *this = reinterpret_steal(obj); +} + +PYBIND11_NOINLINE_ATTR PYBIND11_INLINE void +module_::add_object(const char *name, handle obj, bool overwrite) { + if (!overwrite && hasattr(*this, name)) { + pybind11_fail("Error during initialization: multiple incompatible definitions with name \"" + + std::string(name) + "\""); + } + + PyModule_AddObject(ptr(), name, obj.inc_ref().ptr() /* steals a reference */); +} + +PYBIND11_INLINE module_ module_::create_extension_module(const char *name, + const char *doc, + PyModuleDef *def, + mod_gil_not_used gil_not_used) { + // Placement new (not an allocation). + new (def) PyModuleDef{/* m_base */ PyModuleDef_HEAD_INIT, + /* m_name */ name, + /* m_doc */ options::show_user_defined_docstrings() ? doc : nullptr, + /* m_size */ -1, + /* m_methods */ nullptr, + /* m_slots */ nullptr, + /* m_traverse */ nullptr, + /* m_clear */ nullptr, + /* m_free */ nullptr}; + auto *m = PyModule_Create(def); + if (m == nullptr) { + if (PyErr_Occurred()) { + throw error_already_set(); + } + pybind11_fail("Internal error in module_::create_extension_module()"); + } + if (gil_not_used.flag()) { +#ifdef Py_GIL_DISABLED + PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED); +#endif + } + // TODO: Should be reinterpret_steal for Python 3, but Python also steals it again when + // returned from PyInit_... + // For Python 2, reinterpret_borrow was correct. + return reinterpret_borrow(m); +} + PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 9cb8aee5b6..b53e755296 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -750,59 +750,13 @@ class module_ : public object { py::module_ m2 = m.def_submodule("sub", "A submodule of 'example'"); py::module_ m3 = m2.def_submodule("subsub", "A submodule of 'example.sub'"); \endrst */ - module_ def_submodule(const char *name, const char *doc = nullptr) { - const char *this_name = PyModule_GetName(m_ptr); - if (this_name == nullptr) { - throw error_already_set(); - } - std::string full_name = std::string(this_name) + '.' + name; - handle submodule = PyImport_AddModule(full_name.c_str()); - if (!submodule) { - throw error_already_set(); - } - auto result = reinterpret_borrow(submodule); - if (doc && options::show_user_defined_docstrings()) { - result.attr("__doc__") = pybind11::str(doc); - } - -#if defined(GRAALVM_PYTHON) && (!defined(GRAALPY_VERSION_NUM) || GRAALPY_VERSION_NUM < 0x190000) - // GraalPy doesn't support PyModule_GetFilenameObject, - // so getting by attribute (see PR #5584) - handle this_module = m_ptr; - if (object this_file = getattr(this_module, "__file__", none())) { - result.attr("__file__") = this_file; - } -#else - handle this_file = PyModule_GetFilenameObject(m_ptr); - if (this_file) { - result.attr("__file__") = this_file; - } else if (PyErr_ExceptionMatches(PyExc_SystemError) != 0) { - PyErr_Clear(); - } else { - throw error_already_set(); - } -#endif - attr(name) = result; - return result; - } + module_ def_submodule(const char *name, const char *doc = nullptr); /// Import and return a module or throws `error_already_set`. - static module_ import(const char *name) { - PyObject *obj = PyImport_ImportModule(name); - if (!obj) { - throw error_already_set(); - } - return reinterpret_steal(obj); - } + static module_ import(const char *name); /// Reload the module or throws `error_already_set`. - void reload() { - PyObject *obj = PyImport_ReloadModule(ptr()); - if (!obj) { - throw error_already_set(); - } - *this = reinterpret_steal(obj); - } + void reload(); /** \rst Adds an object to the module using the given name. Throws if an object with the given name @@ -811,15 +765,7 @@ class module_ : public object { ``overwrite`` should almost always be false: attempting to overwrite objects that pybind11 has established will, in most cases, break things. \endrst */ - PYBIND11_NOINLINE void add_object(const char *name, handle obj, bool overwrite = false) { - if (!overwrite && hasattr(*this, name)) { - pybind11_fail( - "Error during initialization: multiple incompatible definitions with name \"" - + std::string(name) + "\""); - } - - PyModule_AddObject(ptr(), name, obj.inc_ref().ptr() /* steals a reference */); - } + void add_object(const char *name, handle obj, bool overwrite = false); // DEPRECATED (since PR #5688): Use PyModuleDef directly instead. using module_def = PyModuleDef; @@ -832,34 +778,7 @@ class module_ : public object { static module_ create_extension_module(const char *name, const char *doc, PyModuleDef *def, - mod_gil_not_used gil_not_used = mod_gil_used()) { - // Placement new (not an allocation). - new (def) PyModuleDef{/* m_base */ PyModuleDef_HEAD_INIT, - /* m_name */ name, - /* m_doc */ options::show_user_defined_docstrings() ? doc : nullptr, - /* m_size */ -1, - /* m_methods */ nullptr, - /* m_slots */ nullptr, - /* m_traverse */ nullptr, - /* m_clear */ nullptr, - /* m_free */ nullptr}; - auto *m = PyModule_Create(def); - if (m == nullptr) { - if (PyErr_Occurred()) { - throw error_already_set(); - } - pybind11_fail("Internal error in module_::create_extension_module()"); - } - if (gil_not_used.flag()) { -#ifdef Py_GIL_DISABLED - PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED); -#endif - } - // TODO: Should be reinterpret_steal for Python 3, but Python also steals it again when - // returned from PyInit_... - // For Python 2, reinterpret_borrow was correct. - return reinterpret_borrow(m); - } + mod_gil_not_used gil_not_used = mod_gil_used()); }; PYBIND11_NAMESPACE_BEGIN(detail) diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index 71cc31b044..88de78d024 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -563,9 +563,7 @@ class PYBIND11_EXPORT_EXCEPTION error_already_set : public std::exception { public: /// Fetches the current Python exception (using PyErr_Fetch()), which will clear the /// current Python error indicator. - error_already_set() - : m_fetched_error{new detail::error_fetch_and_normalize("pybind11::error_already_set"), - m_fetched_error_deleter} {} + error_already_set(); /// The what() result is built lazily on demand. /// WARNING: This member function needs to acquire the Python GIL. This can lead to diff --git a/src/class.cpp b/src/class.cpp index 5418b4e359..4056f74fe7 100644 --- a/src/class.cpp +++ b/src/class.cpp @@ -6,5 +6,6 @@ # error "pybind11 library sources must be compiled with PYBIND11_PRECOMPILED defined." #endif +#include #include #include diff --git a/src/common.cpp b/src/common.cpp index e5c96b162a..d3d82a48a1 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -7,4 +7,5 @@ #endif #include +#include #include diff --git a/src/internals.cpp b/src/internals.cpp index 86a3549507..d5728d7a63 100644 --- a/src/internals.cpp +++ b/src/internals.cpp @@ -7,4 +7,5 @@ #endif #include +#include #include diff --git a/src/type_caster_base.cpp b/src/type_caster_base.cpp index f9119044dc..22ae13fd77 100644 --- a/src/type_caster_base.cpp +++ b/src/type_caster_base.cpp @@ -6,5 +6,6 @@ # error "pybind11 library sources must be compiled with PYBIND11_PRECOMPILED defined." #endif +#include #include #include diff --git a/tests/extra_python_package/test_files.py b/tests/extra_python_package/test_files.py index a14f057a68..10b092024c 100644 --- a/tests/extra_python_package/test_files.py +++ b/tests/extra_python_package/test_files.py @@ -43,6 +43,7 @@ main_headers = { + "include/pybind11/attr-inl.h", "include/pybind11/attr.h", "include/pybind11/buffer_info.h", "include/pybind11/cast.h", @@ -54,6 +55,7 @@ "include/pybind11/embed.h", "include/pybind11/eval.h", "include/pybind11/functional.h", + "include/pybind11/gil-inl.h", "include/pybind11/gil.h", "include/pybind11/gil_safe_call_once.h", "include/pybind11/gil_simple.h", @@ -88,6 +90,7 @@ "include/pybind11/detail/class.h", "include/pybind11/detail/common-inl.h", "include/pybind11/detail/common.h", + "include/pybind11/detail/cpp_conduit-inl.h", "include/pybind11/detail/cpp_conduit.h", "include/pybind11/detail/descr.h", "include/pybind11/detail/dynamic_raw_ptr_cast_if_possible.h", @@ -102,6 +105,7 @@ "include/pybind11/detail/struct_smart_holder.h", "include/pybind11/detail/type_caster_base-inl.h", "include/pybind11/detail/type_caster_base.h", + "include/pybind11/detail/typeid-inl.h", "include/pybind11/detail/typeid.h", "include/pybind11/detail/using_smart_holder.h", "include/pybind11/detail/value_and_holder.h", diff --git a/tools/check_inl_headers.py b/tools/check_inl_headers.py index 6787fb740b..98e5d458bc 100755 --- a/tools/check_inl_headers.py +++ b/tools/check_inl_headers.py @@ -17,9 +17,11 @@ # Same in the library and in the modules that link it. ENVIRONMENT_MACROS = { + "GRAALPY_VERSION_NUM", "GRAALVM_PYTHON", "NDEBUG", "PYBIND11_BUILTIN_QUALNAME", + "PYBIND11_HAS_CXXABI_H", "PYBIND11_HAS_STRING_VIEW", "PYBIND11_PRECOMPILED", "PYPY_VERSION",