-
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 various minor memory leaks in the tests (found by Valgrind in #2746) #2758
Conversation
…d for a read-only object, and enable test_buffer.py
@@ -550,6 +550,12 @@ extern "C" inline int pybind11_getbuffer(PyObject *obj, Py_buffer *view, int fla | |||
} | |||
std::memset(view, 0, sizeof(Py_buffer)); | |||
buffer_info *info = tinfo->get_buffer(obj, tinfo->get_buffer_data); | |||
if ((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE && info->readonly) { | |||
delete info; |
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.
Comment from #2746:
- @YannickJadoul: This was the actual leak; The fragment was just moved up to not do futile work and such that view->obj = nullptr; isn't necessary anymore.
@@ -86,13 +86,13 @@ TEST_SUBMODULE(stl_binders, m) { | |||
|
|||
// test_noncopyable_containers | |||
py::bind_vector<std::vector<E_nc>>(m, "VectorENC"); | |||
m.def("get_vnc", &one_to_n<std::vector<E_nc>>, py::return_value_policy::reference); |
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.
Comments from #2746:
- @bstaletic: These rvps look bogus. The return is by value.
- @YannickJadoul: Huh? I thought it returned a pointer to the container?
- @bstaletic: I was just entirely unable to figure out why/how the reference rvp is here. This test was added in Fix stl_bind to support movable, non-copyable value types #490 (commit 617fbcf). The PR involves returning elements by reference, but I don't see why these factory methods return the container by reference.
- @YannickJadoul: Huh? I thought it returned a pointer to the container?
- @bstaletic: That's what happens when you (read: I) do a review while really really tired. Listen to @YannickJadoul, everyone!
- @YannickJadoul: Hah 😄 No worries, we've all been there.
- @bstaletic: I'm not resolving this discussion, just so it doesn't get collapsed before others take a look.
@@ -231,7 +231,8 @@ TEST_SUBMODULE(class_, m) { | |||
}; | |||
|
|||
auto def = new PyMethodDef{"f", f, METH_VARARGS, nullptr}; | |||
return py::reinterpret_steal<py::object>(PyCFunction_NewEx(def, nullptr, m.ptr())); | |||
py::capsule def_capsule(def, [](void *ptr) { delete reinterpret_cast<PyMethodDef *>(ptr); }); | |||
return py::reinterpret_steal<py::object>(PyCFunction_NewEx(def, def_capsule.ptr(), m.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.
PyCFunction_NewEx()
just calls PyCMethod_new()
with the last parameter set to NULL.
What this changes is m_self
from NULL to def_capsule.ptr()
.
Beyond that, I have no idea how this works. PyCFunction*
aren't documented, which is discussed in bpo-16776.
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.
What this changes is
m_self
from NULL todef_capsule.ptr()
.
We already do this in the actual pybind11 code, btw.
Alternatively, if you prefer, we could play the "weakref
trick", here? But I thought this would be easier, since AFAICT m_self
being NULL
wasn't the point of this test.
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.
I'm just trying to understand how does setting m_self
work to our benefit in this case. Does it steal the reference or borrow it? Is our reference counting right in this case? If it appeased valgrind, I guess it's correct, I'd just like a better understanding of how it works.
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.
It does borrow, yes, and keeps it alive as some kind of CPython-style void *data
, passed as first argument to our cpp_function::dispatcher
:
pybind11/include/pybind11/pybind11.h
Line 492 in d587a2f
static PyObject *dispatcher(PyObject *self, PyObject *args_in, PyObject *kwargs_in) { |
See here for the other case where CPython uses it:
pybind11/include/pybind11/pybind11.h
Line 372 in d587a2f
m_ptr = PyCFunction_NewEx(rec->def, rec_capsule.ptr(), scope_module.ptr()); |
This mechanism was also our problem with Python 3.9.0, m_self
being decref'ed a few lines too early while pybind11 was still accessing it, in #2576.
// #2742: Don't expect ownership of raw pointer to `new`ed object to be transferred with `py::return_value_policy::move` | ||
m.def("get_moveissue1", [](int i) { return std::unique_ptr<MoveIssue1>(new MoveIssue1(i)); }, py::return_value_policy::move); |
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 make things clear, this is avoiding the issue reported in #2742, but not proposing a definite solution.
Moreover, the issue that was a precursor to adding this tests doesn't seem to depend on get_moveissue1()
returning a raw owning pointer.
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.
No, but if we decide to have a different approach to #2742, we'll add new tests for those, anyway? So this change could be semi-permanent?
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 looks good to me.
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.
Small fix then mostly test changes to be tidy, looks good to me.
Looks like a (natural) mistake to me. |
OK, then I'll merge and rebase! Thanks for checking! |
Description
Five reasonably trivial fixes for leaks in the tests, commits carved out of #2746:
Suggested changelog entry: