Skip to content
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

Merged
merged 5 commits into from
Jan 1, 2021

Conversation

YannickJadoul
Copy link
Collaborator

@YannickJadoul YannickJadoul commented Dec 30, 2020

Description

Five reasonably trivial fixes for leaks in the tests, commits carved out of #2746:

  • Fix leak in the test_copy_move::test_move_fallback
  • Fix leaking PyMethodDef in test_class::test_implicit_conversion_life_support
  • Plumb leak in test_buffer, occuring when a mutable buffer is requested for a read-only object, and enable test_buffer.py
  • Fix weird return_value_policy::reference in test_stl_binders, and enable those tests
  • Cleanup nodelete holder objects in test_smart_ptr, and enable those tests

Suggested changelog entry:

Fixed various minor memory leaks in pybind11's test suite.

@@ -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;
Copy link
Collaborator Author

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);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comments from #2746:

@@ -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()));
Copy link
Collaborator

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.

Copy link
Collaborator Author

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 to def_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.

Copy link
Collaborator

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.

Copy link
Collaborator Author

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:

static PyObject *dispatcher(PyObject *self, PyObject *args_in, PyObject *kwargs_in) {

See here for the other case where CPython uses it:

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.

Comment on lines +217 to +218
// #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);
Copy link
Collaborator

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.

Copy link
Collaborator Author

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?

Copy link
Collaborator

@bstaletic bstaletic left a 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.

Copy link
Collaborator

@henryiii henryiii left a 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.

@YannickJadoul
Copy link
Collaborator Author

@henryiii, you can't think why these test_stl_binders tests were returning with return_value_policy::reference, in #490 / 378debb?
If not, I think we can merge; the other fixes should be uncontroversial enough :-)

@henryiii
Copy link
Collaborator

henryiii commented Jan 1, 2021

Looks like a (natural) mistake to me.

@YannickJadoul
Copy link
Collaborator Author

OK, then I'll merge and rebase! Thanks for checking!

@YannickJadoul YannickJadoul merged commit e57dd47 into pybind:master Jan 1, 2021
@YannickJadoul YannickJadoul deleted the various-leaks branch January 1, 2021 16:05
@github-actions github-actions bot added the needs changelog Possibly needs a changelog entry label Jan 1, 2021
@YannickJadoul YannickJadoul added this to the v2.6.2 milestone Jan 1, 2021
@henryiii henryiii removed the needs changelog Possibly needs a changelog entry label Jan 25, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants