-
Notifications
You must be signed in to change notification settings - Fork 2.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix Python object lifetimes associated with shared_ptrs #1566
Conversation
Thanks for this solution to the problem, I used it in a multithreaded situation and ran into segfaults during destruction from the C++ side. Turns out that if the last pointer gets destroyed from another thread, the Python object may be cleaned up without having the GIL. This will caused reproducible segfaults in my case. Modifying line 1552 to provide a de-allocator lambda like: auto py_obj_ptr = std::shared_ptr<py::object> (new py::object{py_obj}, [](auto py_object_ptr) {
pybind11::gil_scoped_acquire gil;
delete py_object_ptr;
}); Will ensure we hold the GIL when we delete the Python object. In my case it did not matter whether that object had a |
@iwanders - good point! I've incorporated that. For a horrible moment I thought I would have to move gil_scoped_acquire to its own header to work around a circular dependency, but at instantiation time the definition of that class has already been seen, so I settled for a forward declaration. |
Is this line of work still active? I'm really interested in a solution to this problem and before I copy paste or taylor my own hack, I would like to know what are the chances of something like this being incorporated to mainstream. Thanks so much! |
print("Right one", 42) | ||
|
||
holder = m.SharedPtrHolder(Derived()) | ||
# At this point, our 'Derived' instance has gone out of scope in Python but |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to be sure, could you add a pytest.gc_collect()
here?
# At this point, our 'Derived' instance has gone out of scope in Python but | ||
# is being kept alive by a shared_ptr inside 'holder'. | ||
holder.run(42) | ||
assert somelist == [42] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a way that you can check and ensure that all objects get destroyed when the go out of scope?
See usages of ConstructorStats
for how to do so - I'm happy to point out more examples!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(This is to address your point on limiting scope, or at least documenting the scope in code)
I'm still not sure of is if this creates a sort of reference cycle, and effectively cannot be GC'd until the interpreter dies... If it does this, it'd be nice to show it by testing the number of instances alive?
This looks great! FWIW I had started out writing a repro on |
I agree that this is a severe problem that we should definitely fix. One aspect to consider is that only a small subset of users that rely on I think it may be possible to find out whether the underlying type uses a trampoline or not and only run the special case then. See in particular the Other aspects: running the full base caster to make a Quite a bit of this code doesn’t depend on the template type. One issue with a template library like pybind11 is that this code will be replicated thousands of times when creating bindings for large codebases. Thus, whenever possible, it’s very useful to factor out things into non-templated functions that are called from more specific template classes. |
Hi @wjakob thanks for taking a look! As I say in the original PR, it's just a test and a straw man solution; no doubt it can be much improved. Likewise thank you to @eacousineau for taking a look and for suggestions. Where should we go from here? Do you or one of the more regular developers want to have a go at improving this fix, or coming up with a different one? Or I can have a go at improving it? I'm more than happy to leave it to an expert though. |
This functionality requires the `shared_ptr` branch of libcommute. Unfortunately, pybind11 is not quite ready for this. pybind/pybind11#1389 pybind/pybind11#1566
Those methods are meant to be called only by Python classes derived from `generator`. Supporting derived generator classes is too painful with current pybind11 (pybind/pybind11#1566), so I have decided to disallow this functionality altogether.
Those methods are meant to be called only by Python classes derived from `generator`. Supporting derived generator classes is too painful with current pybind11 (pybind/pybind11#1566), so I have decided to disallow this functionality altogether.
Hadn't noticed this previously, but superceded by #2839 |
Let's close this in favour of #2839 - thanks to everyone who commented, hopefully the other PR will be merged soon! |
This is a straw man solution to #1546, based on the patch included in the report. It also fixes #1333 and #1389 (note that "I was deleted" doesn't appear unless you conduct the test inside function scope).
Probably a pre-requisite for #1552 but does not fix it (I have to be careful wording this or github gets the wrong idea).
Questions: