Skip to content

Commit a021e04

Browse files
committed
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.
1 parent 595f7f4 commit a021e04

File tree

3 files changed

+44
-21
lines changed

3 files changed

+44
-21
lines changed

include/pybind11/detail/smart_holder_type_casters.h

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "type_caster_base.h"
1919
#include "typeid.h"
2020

21+
#include <cassert>
2122
#include <cstddef>
2223
#include <memory>
2324
#include <new>
@@ -343,6 +344,18 @@ struct smart_holder_type_caster_class_hooks : smart_holder_type_caster_base_tag
343344
}
344345
};
345346

347+
struct shared_ptr_trampoline_self_life_support {
348+
PyObject *self;
349+
explicit shared_ptr_trampoline_self_life_support(instance *inst)
350+
: self{reinterpret_cast<PyObject *>(inst)} {
351+
Py_INCREF(self);
352+
}
353+
void operator()(void *) {
354+
gil_scoped_acquire gil;
355+
Py_DECREF(self);
356+
}
357+
};
358+
346359
template <typename T>
347360
struct smart_holder_type_caster_load {
348361
using holder_type = pybindit::memory::smart_holder;
@@ -376,14 +389,6 @@ struct smart_holder_type_caster_load {
376389
return *raw_ptr;
377390
}
378391

379-
struct shared_ptr_dec_ref_deleter {
380-
PyObject *self;
381-
void operator()(void *) {
382-
gil_scoped_acquire gil;
383-
Py_DECREF(self);
384-
}
385-
};
386-
387392
std::shared_ptr<T> loaded_as_shared_ptr() const {
388393
if (load_impl.unowned_void_ptr_from_direct_conversion != nullptr)
389394
throw cast_error("Unowned pointer from direct conversion cannot be converted to a"
@@ -404,24 +409,26 @@ struct smart_holder_type_caster_load {
404409
std::shared_ptr<void> released_ptr = vptr_gd_ptr->released_ptr.lock();
405410
if (released_ptr)
406411
return std::shared_ptr<T>(released_ptr, type_raw_ptr);
407-
auto self = reinterpret_cast<PyObject *>(load_impl.loaded_v_h.inst);
408-
Py_INCREF(self);
409-
std::shared_ptr<T> to_be_released(type_raw_ptr, shared_ptr_dec_ref_deleter{self});
412+
std::shared_ptr<T> to_be_released(
413+
type_raw_ptr,
414+
shared_ptr_trampoline_self_life_support(load_impl.loaded_v_h.inst));
410415
vptr_gd_ptr->released_ptr = to_be_released;
411416
return to_be_released;
412417
}
413-
if (std::get_deleter<shared_ptr_dec_ref_deleter>(hld.vptr) != nullptr) {
418+
auto sptsls_ptr = std::get_deleter<shared_ptr_trampoline_self_life_support>(hld.vptr);
419+
if (sptsls_ptr != nullptr) {
414420
// This code is reachable only if there are multiple registered_instances for the
415421
// same pointee.
416-
// SMART_HOLDER_WIP: keep weak_ref
417-
std::shared_ptr<void> void_shd_ptr = hld.template as_shared_ptr<void>();
418-
return std::shared_ptr<T>(void_shd_ptr, type_raw_ptr);
422+
assert(reinterpret_cast<PyObject *>(load_impl.loaded_v_h.inst)
423+
!= sptsls_ptr->self);
424+
return std::shared_ptr<T>(
425+
type_raw_ptr,
426+
shared_ptr_trampoline_self_life_support(load_impl.loaded_v_h.inst));
419427
}
420428
if (!pybindit::memory::type_has_shared_from_this(type_raw_ptr)) {
421-
// SMART_HOLDER_WIP: keep weak_ref
422-
auto self = reinterpret_cast<PyObject *>(load_impl.loaded_v_h.inst);
423-
Py_INCREF(self);
424-
return std::shared_ptr<T>(type_raw_ptr, shared_ptr_dec_ref_deleter{self});
429+
return std::shared_ptr<T>(
430+
type_raw_ptr,
431+
shared_ptr_trampoline_self_life_support(load_impl.loaded_v_h.inst));
425432
}
426433
if (hld.vptr_is_external_shared_ptr) {
427434
pybind11_fail("smart_holder_type_casters loaded_as_shared_ptr failure: not "

tests/test_class_sh_trampoline_shared_from_this.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,20 @@ def test_multiple_registered_instances_for_same_pointee():
170170
assert obj_pt.attachment_in_dict == "Obj0"
171171
else:
172172
assert not hasattr(obj_pt, "attachment_in_dict")
173+
assert obj0.history == "PySft"
174+
break # Comment out for manual leak checking (use `top` command).
175+
176+
177+
def test_multiple_registered_instances_for_same_pointee_leak():
178+
obj0 = PySft("")
179+
while True:
180+
stash1 = m.SftSharedPtrStash(1)
181+
stash1.Add(m.Sft(obj0))
182+
assert stash1.use_count(0) == 1
183+
stash1.Add(m.Sft(obj0))
184+
assert stash1.use_count(0) == 1
185+
assert stash1.use_count(1) == 1
186+
assert obj0.history == ""
173187
break # Comment out for manual leak checking (use `top` command).
174188

175189

tests/test_class_sh_trampoline_shared_ptr_cpp_arg.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ def test_infinite():
130130
tester = m.SpBaseTester()
131131
while True:
132132
tester.set_object(m.SpBase())
133-
return # Comment out for manual leak checking (use `top` command).
133+
break # Comment out for manual leak checking (use `top` command).
134134

135135

136136
def test_std_make_shared_factory():
@@ -139,4 +139,6 @@ def __init__(self):
139139
super(PyChild, self).__init__(0)
140140

141141
obj = PyChild()
142-
assert m.pass_through_shd_ptr(obj) is obj
142+
while True:
143+
assert m.pass_through_shd_ptr(obj) is obj
144+
break # Comment out for manual leak checking (use `top` command).

0 commit comments

Comments
 (0)