-
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 refcounting for tp_base objects of new types #950
Fix refcounting for tp_base objects of new types #950
Conversation
To fix a difficult-to-reproduce segfault on Python interpreter exit, ensure that the tp_base field of a handful of new heap-types is counted as a reference to that base type object.
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.
Looks like a good catch and fix, with a couple very minor changes.
include/pybind11/class_support.h
Outdated
@@ -27,6 +27,10 @@ extern "C" inline int pybind11_static_set(PyObject *self, PyObject *obj, PyObjec | |||
return PyProperty_Type.tp_descr_set(self, cls, value); | |||
} | |||
|
|||
static PyTypeObject *type_with_ref_incd(PyTypeObject *type) { |
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.
Doesn't need to be static
, but rather inline
.
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.
Also, this needs to be moved outside the #if !defined(PYPY_VERSION)
as it gets used later on under PyPy as well.
include/pybind11/class_support.h
Outdated
@@ -27,6 +27,10 @@ extern "C" inline int pybind11_static_set(PyObject *self, PyObject *obj, PyObjec | |||
return PyProperty_Type.tp_descr_set(self, cls, value); | |||
} | |||
|
|||
static PyTypeObject *type_with_ref_incd(PyTypeObject *type) { | |||
return (PyTypeObject *)(handle((PyObject *)(type)).inc_ref().ptr()); |
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 should be equivalent, but it's a bit more self-documenting:
return (PyTypeObject *) reinterpret_borrow<object>((PyObject *) type).release().ptr();
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.
Actually, scracth that: it's much simpler to just bypass the pybind helper classes completely here:
Py_INCREF(type);
return type;
Thanks for the feedback. I've implemented your suggestions as individual commits for clarity, but I can squash down to a single commit later if preferred. |
No need to squash the PR (for a single-commit PR github has a "squash and merge" button). |
For consistency, how about also using |
Great refcounting detective work by the way 👍 |
Replace existing reference-handling with call to type_incref() when setting tp_base.
@wjakob: Thanks for the feedback (and kind words). I've pushed new commits implementing your suggestions. |
This looks good to merge to me (assuming that the CI bots finish successfully). |
Merged. Thanks for tracking this down and the PR! |
Hi,
I recently hit segfaults on Python exit while using a pybind11-based module. I have found and fixed a bug in it, but while investigating, found what I think are bugs in pybind11 too. I think the code in
make_static_property_type()
needs toinc_ref()
PyProperty_Type
because of its use as thetp_base
of the new type; similarly for other assignments to a new type'stp_base
. In my case, enough refs were being destroyed that on exit, Python tried to deallocPyProperty_Type
, with bad results.In pure Python, creating a heap-subtype of 'property' increases the refcount of
property
(i.e.,PyProperty_Type
) by 3:This makes sense; the three new references in
st
are: the__base__
; the sole entry in the__bases__
tuple; the entry in the__mro__
.However, importing a trivial pybind11-based module only increases the refcount by 2 even though importing the module also creates a heap-subtype of property (in
make_static_property_type()
):# ... compile into 'empty_struct' module ...
While reading around, I found Python issue 980082 which suggests a new type's
tp_base
ought to beinc_ref()
'd. Also, pybind11'smake_new_python_type()
doesinc_ref()
thetp_base
of the new type.This PR
inc_ref()
s the other three assignments to a new type'stp_base
. This seemed just about worth making a tiny function for.I've targeted
master
with this PR but should it also be considered for version branches?Thanks!