Skip to content

Commit 299041d

Browse files
committed
C++/Python shared object crash fix
C.f. pybind#1546 (comment)
1 parent 486e136 commit 299041d

File tree

1 file changed

+57
-2
lines changed

1 file changed

+57
-2
lines changed

include/pybind11/cast.h

+57-2
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@
3333
#endif
3434

3535
NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
36+
37+
class gil_scoped_acquire;
38+
class gil_scoped_release;
39+
3640
NAMESPACE_BEGIN(detail)
3741

3842
/// A life support system for temporary objects created by `type_caster::load()`.
@@ -1510,9 +1514,55 @@ struct copyable_holder_caster : public type_caster_base<type> {
15101514
holder_type holder;
15111515
};
15121516

1517+
/// Shared object crash fix: https://github.com/pybind/pybind11/issues/1546#issuecomment-526712704
1518+
1519+
// /// Specialize for the common std::shared_ptr, so users don't need to
1520+
// template <typename T>
1521+
// class type_caster<std::shared_ptr<T>> : public copyable_holder_caster<T, std::shared_ptr<T>> { };
1522+
15131523
/// Specialize for the common std::shared_ptr, so users don't need to
15141524
template <typename T>
1515-
class type_caster<std::shared_ptr<T>> : public copyable_holder_caster<T, std::shared_ptr<T>> { };
1525+
class type_caster<std::shared_ptr<T>> {
1526+
1527+
PYBIND11_TYPE_CASTER (std::shared_ptr<T>, _(PYBIND11_STRING_NAME));
1528+
1529+
using BaseCaster = copyable_holder_caster<T, std::shared_ptr<T>>;
1530+
1531+
bool load (pybind11::handle src, bool b)
1532+
{
1533+
BaseCaster bc;
1534+
bool success = bc.load (src, b);
1535+
if (!success)
1536+
{
1537+
return false;
1538+
}
1539+
1540+
auto base_ptr = static_cast<std::shared_ptr<T>> (bc);
1541+
auto h = BaseCaster::cast(base_ptr, return_value_policy(), handle());
1542+
auto py_obj = reinterpret_borrow<object>(h);
1543+
1544+
auto py_obj_ptr = std::shared_ptr<object>{
1545+
new object{py_obj},
1546+
[](auto py_object_ptr) {
1547+
// It's possible that when the shared_ptr dies we won't have the
1548+
// gil (if the last holder is in a non-Python thread), so we
1549+
// make sure to acquire it in the deleter.
1550+
gil_scoped_acquire gil;
1551+
delete py_object_ptr;
1552+
}
1553+
};
1554+
1555+
value = std::shared_ptr<T> (py_obj_ptr, base_ptr.get ());
1556+
return true;
1557+
}
1558+
1559+
static handle cast (std::shared_ptr<T> sp,
1560+
return_value_policy rvp,
1561+
handle h)
1562+
{
1563+
return BaseCaster::cast (sp, rvp, h);
1564+
}
1565+
};
15161566

15171567
template <typename type, typename holder_type>
15181568
struct move_only_holder_caster {
@@ -1554,6 +1604,11 @@ template <typename base, typename holder> struct is_holder_type :
15541604
template <typename base, typename deleter> struct is_holder_type<base, std::unique_ptr<base, deleter>> :
15551605
std::true_type {};
15561606

1607+
// Specialization for shared_ptr:
1608+
template <typename base> struct is_holder_type<base, std::shared_ptr<base>> :
1609+
std::true_type {};
1610+
1611+
15571612
template <typename T> struct handle_type_name { static constexpr auto name = _<T>(); };
15581613
template <> struct handle_type_name<bytes> { static constexpr auto name = _(PYBIND11_BYTES_NAME); };
15591614
template <> struct handle_type_name<args> { static constexpr auto name = _("*args"); };
@@ -1901,7 +1956,7 @@ class argument_loader {
19011956
static constexpr bool has_kwargs = kwargs_pos < 0;
19021957
static constexpr bool has_args = args_pos < 0;
19031958

1904-
static constexpr auto arg_names = detail::concat(type_descr(make_caster<Args>::name)...);
1959+
static constexpr auto arg_names = concat(type_descr(make_caster<Args>::name)...);
19051960

19061961
bool load_args(function_call &call) {
19071962
return load_impl_sequence(call, indices{});

0 commit comments

Comments
 (0)