diff --git a/docs/advanced.rst b/docs/advanced.rst index 0a84a4554..a050dfbe6 100644 --- a/docs/advanced.rst +++ b/docs/advanced.rst @@ -663,10 +663,13 @@ In addition to the above return value policies, further `call policies` can be specified to indicate dependencies between parameters. There is currently just one policy named ``keep_alive``, which indicates that the argument with index ``Patient`` should be kept alive at least until the -argument with index ``Nurse`` is freed by the garbage collector; argument -indices start at one, while zero refers to the return value. For methods, index -one refers to the implicit ``this`` pointer, while regular arguments begin at -index two. Arbitrarily many call policies can be specified. +argument with index ``Nurse`` is freed by the garbage collector, as long as the +nurse object supports weak references (pybind11 extension classes all support +weak references). If the nurse object does not support weak references and is +not None an appropriate exception will be thrown. Argument indices start at +one, while zero refers to the return value. For methods, index one refers to +the implicit ``this`` pointer, while regular arguments begin at index two. +Arbitrarily many call policies can be specified. Consider the following example: the binding code for a list append operation that ties the lifetime of the newly added element to the underlying container diff --git a/example/example-keep-alive.cpp b/example/example-keep-alive.cpp index c099aa81f..c2aeaeefa 100644 --- a/example/example-keep-alive.cpp +++ b/example/example-keep-alive.cpp @@ -22,6 +22,7 @@ public: ~Parent() { std::cout << "Releasing parent." << std::endl; } void addChild(Child *) { } Child *returnChild() { return new Child(); } + Child *returnNullChild() { return nullptr; } }; void init_ex_keep_alive(py::module &m) { @@ -30,7 +31,9 @@ void init_ex_keep_alive(py::module &m) { .def("addChild", &Parent::addChild) .def("addChildKeepAlive", &Parent::addChild, py::keep_alive<1, 2>()) .def("returnChild", &Parent::returnChild) - .def("returnChildKeepAlive", &Parent::returnChild, py::keep_alive<1, 0>()); + .def("returnChildKeepAlive", &Parent::returnChild, py::keep_alive<1, 0>()) + .def("returnNullChildKeepAliveChild", &Parent::returnNullChild, py::keep_alive<1, 0>()) + .def("returnNullChildKeepAliveParent", &Parent::returnNullChild, py::keep_alive<0, 1>()); py::class_(m, "Child") .def(py::init<>()); diff --git a/example/example-keep-alive.py b/example/example-keep-alive.py index ad0176ed4..187ad534f 100644 --- a/example/example-keep-alive.py +++ b/example/example-keep-alive.py @@ -31,6 +31,7 @@ if True: gc.collect() print(p) p = None + gc.collect() print("") @@ -41,6 +42,26 @@ if True: print(p) p = None +gc.collect() +print("") + +if True: + p = Parent() + p.returnNullChildKeepAliveChild() + gc.collect() + print(p) + p = None + +gc.collect() +print("") + +if True: + p = Parent() + p.returnNullChildKeepAliveParent() + gc.collect() + print(p) + p = None + gc.collect() print("") print("Terminating..") diff --git a/example/example-keep-alive.ref b/example/example-keep-alive.ref index 7eb02c51f..775aff418 100644 --- a/example/example-keep-alive.ref +++ b/example/example-keep-alive.ref @@ -22,4 +22,12 @@ Allocating child. Releasing parent. Releasing child. +Allocating parent. + +Releasing parent. + +Allocating parent. + +Releasing parent. + Terminating.. diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 427b12c3e..d82ae926e 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1098,8 +1098,8 @@ inline void keep_alive_impl(handle nurse, handle patient) { if (!nurse || !patient) pybind11_fail("Could not activate keep_alive!"); - if (patient.ptr() == Py_None) - return; /* Nothing to keep alive */ + if (patient.ptr() == Py_None || nurse.ptr() == Py_None) + return; /* Nothing to keep alive or nothing to be kept alive by */ cpp_function disable_lifesupport( [patient](handle weakref) { patient.dec_ref(); weakref.dec_ref(); });