Adding test_multiple_registered_instances_for_same_pointee_leak. Subtle changes in smart_holder_type_caster_load, trying to work on weak_ptr for shared_ptr_trampoline_self_life_support, but that didn't work out. Manually fully leak-checked again.

This commit is contained in:
Ralf W. Grosse-Kunstleve 2021-06-29 15:17:11 -07:00 committed by Ralf W. Grosse-Kunstleve
parent 595f7f4f6e
commit a021e045a9
3 changed files with 44 additions and 21 deletions

View File

@ -18,6 +18,7 @@
#include "type_caster_base.h" #include "type_caster_base.h"
#include "typeid.h" #include "typeid.h"
#include <cassert>
#include <cstddef> #include <cstddef>
#include <memory> #include <memory>
#include <new> #include <new>
@ -343,6 +344,18 @@ struct smart_holder_type_caster_class_hooks : smart_holder_type_caster_base_tag
} }
}; };
struct shared_ptr_trampoline_self_life_support {
PyObject *self;
explicit shared_ptr_trampoline_self_life_support(instance *inst)
: self{reinterpret_cast<PyObject *>(inst)} {
Py_INCREF(self);
}
void operator()(void *) {
gil_scoped_acquire gil;
Py_DECREF(self);
}
};
template <typename T> template <typename T>
struct smart_holder_type_caster_load { struct smart_holder_type_caster_load {
using holder_type = pybindit::memory::smart_holder; using holder_type = pybindit::memory::smart_holder;
@ -376,14 +389,6 @@ struct smart_holder_type_caster_load {
return *raw_ptr; return *raw_ptr;
} }
struct shared_ptr_dec_ref_deleter {
PyObject *self;
void operator()(void *) {
gil_scoped_acquire gil;
Py_DECREF(self);
}
};
std::shared_ptr<T> loaded_as_shared_ptr() const { std::shared_ptr<T> loaded_as_shared_ptr() const {
if (load_impl.unowned_void_ptr_from_direct_conversion != nullptr) if (load_impl.unowned_void_ptr_from_direct_conversion != nullptr)
throw cast_error("Unowned pointer from direct conversion cannot be converted to a" throw cast_error("Unowned pointer from direct conversion cannot be converted to a"
@ -404,24 +409,26 @@ struct smart_holder_type_caster_load {
std::shared_ptr<void> released_ptr = vptr_gd_ptr->released_ptr.lock(); std::shared_ptr<void> released_ptr = vptr_gd_ptr->released_ptr.lock();
if (released_ptr) if (released_ptr)
return std::shared_ptr<T>(released_ptr, type_raw_ptr); return std::shared_ptr<T>(released_ptr, type_raw_ptr);
auto self = reinterpret_cast<PyObject *>(load_impl.loaded_v_h.inst); std::shared_ptr<T> to_be_released(
Py_INCREF(self); type_raw_ptr,
std::shared_ptr<T> to_be_released(type_raw_ptr, shared_ptr_dec_ref_deleter{self}); shared_ptr_trampoline_self_life_support(load_impl.loaded_v_h.inst));
vptr_gd_ptr->released_ptr = to_be_released; vptr_gd_ptr->released_ptr = to_be_released;
return to_be_released; return to_be_released;
} }
if (std::get_deleter<shared_ptr_dec_ref_deleter>(hld.vptr) != nullptr) { auto sptsls_ptr = std::get_deleter<shared_ptr_trampoline_self_life_support>(hld.vptr);
if (sptsls_ptr != nullptr) {
// This code is reachable only if there are multiple registered_instances for the // This code is reachable only if there are multiple registered_instances for the
// same pointee. // same pointee.
// SMART_HOLDER_WIP: keep weak_ref assert(reinterpret_cast<PyObject *>(load_impl.loaded_v_h.inst)
std::shared_ptr<void> void_shd_ptr = hld.template as_shared_ptr<void>(); != sptsls_ptr->self);
return std::shared_ptr<T>(void_shd_ptr, type_raw_ptr); return std::shared_ptr<T>(
type_raw_ptr,
shared_ptr_trampoline_self_life_support(load_impl.loaded_v_h.inst));
} }
if (!pybindit::memory::type_has_shared_from_this(type_raw_ptr)) { if (!pybindit::memory::type_has_shared_from_this(type_raw_ptr)) {
// SMART_HOLDER_WIP: keep weak_ref return std::shared_ptr<T>(
auto self = reinterpret_cast<PyObject *>(load_impl.loaded_v_h.inst); type_raw_ptr,
Py_INCREF(self); shared_ptr_trampoline_self_life_support(load_impl.loaded_v_h.inst));
return std::shared_ptr<T>(type_raw_ptr, shared_ptr_dec_ref_deleter{self});
} }
if (hld.vptr_is_external_shared_ptr) { if (hld.vptr_is_external_shared_ptr) {
pybind11_fail("smart_holder_type_casters loaded_as_shared_ptr failure: not " pybind11_fail("smart_holder_type_casters loaded_as_shared_ptr failure: not "

View File

@ -170,6 +170,20 @@ def test_multiple_registered_instances_for_same_pointee():
assert obj_pt.attachment_in_dict == "Obj0" assert obj_pt.attachment_in_dict == "Obj0"
else: else:
assert not hasattr(obj_pt, "attachment_in_dict") assert not hasattr(obj_pt, "attachment_in_dict")
assert obj0.history == "PySft"
break # Comment out for manual leak checking (use `top` command).
def test_multiple_registered_instances_for_same_pointee_leak():
obj0 = PySft("")
while True:
stash1 = m.SftSharedPtrStash(1)
stash1.Add(m.Sft(obj0))
assert stash1.use_count(0) == 1
stash1.Add(m.Sft(obj0))
assert stash1.use_count(0) == 1
assert stash1.use_count(1) == 1
assert obj0.history == ""
break # Comment out for manual leak checking (use `top` command). break # Comment out for manual leak checking (use `top` command).

View File

@ -130,7 +130,7 @@ def test_infinite():
tester = m.SpBaseTester() tester = m.SpBaseTester()
while True: while True:
tester.set_object(m.SpBase()) tester.set_object(m.SpBase())
return # Comment out for manual leak checking (use `top` command). break # Comment out for manual leak checking (use `top` command).
def test_std_make_shared_factory(): def test_std_make_shared_factory():
@ -139,4 +139,6 @@ def test_std_make_shared_factory():
super(PyChild, self).__init__(0) super(PyChild, self).__init__(0)
obj = PyChild() obj = PyChild()
assert m.pass_through_shd_ptr(obj) is obj while True:
assert m.pass_through_shd_ptr(obj) is obj
break # Comment out for manual leak checking (use `top` command).