-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Using py::keep_alive<N, P>()
may cause internals.patients[nurse]
to grow without bounds
#1251
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
Comments
Ah, I see what you mean: #include <pybind11/pybind11.h>
namespace py = pybind11;
struct Foo {};
PYBIND11_MODULE(ka, m) {
m.def("f", [](py::object o1, py::object o2) {}, py::keep_alive<1, 2>());
py::class_<Foo>(m, "Foo")
.def(py::init<>())
;
m.def("patients", [](py::object nurse) {
auto &internals = py::detail::get_internals();
py::list ret;
for (auto p : internals.patients[nurse.ptr()]) {
ret.append(py::reinterpret_borrow<py::object>(p));
}
return ret;
});
} results in: >>> import ka
>>> nurse = ka.Foo()
>>> p1 = ka.Foo()
>>> p2 = ka.Foo()
>>> nurse
<ka.Foo object at 0x7f11a538ddc0>
>>> p1
<ka.Foo object at 0x7f11a53180d8>
>>> p2
<ka.Foo object at 0x7f11a5318148>
>>> ka.f(nurse, p1)
>>> ka.patients(nurse)
[<ka.Foo object at 0x7f11a53180d8>]
>>> ka.f(nurse, p1)
>>> ka.patients(nurse)
[<ka.Foo object at 0x7f11a53180d8>, <ka.Foo object at 0x7f11a53180d8>]
>>> ka.f(nurse, p1)
>>> ka.patients(nurse)
[<ka.Foo object at 0x7f11a53180d8>, <ka.Foo object at 0x7f11a53180d8>, <ka.Foo object at 0x7f11a53180d8>]
>>> ka.f(nurse, p1)
>>> ka.patients(nurse)
[<ka.Foo object at 0x7f11a53180d8>, <ka.Foo object at 0x7f11a53180d8>, <ka.Foo object at 0x7f11a53180d8>, <ka.Foo object at 0x7f11a53180d8>]
>>> ka.f(nurse, p1)
>>> ka.patients(nurse)
[<ka.Foo object at 0x7f11a53180d8>, <ka.Foo object at 0x7f11a53180d8>, <ka.Foo object at 0x7f11a53180d8>, <ka.Foo object at 0x7f11a53180d8>, <ka.Foo object at 0x7f11a53180d8>] I think the best solution here is to change the I suppose we could include the linear search you proposed in 2.2.2, and separately fix it in |
The stored vector of pybind-registered-type patients can grow without bound if a function is called with the same patient multiple times. This commit changes the pybind internal patient storage to an `unordered_set` to avoid the issue. Fixes pybind#1251
The stored vector of pybind-registered-type patients can grow without bound if a function is called with the same patient multiple times. This commit changes the pybind internal patient storage to an `unordered_set` to avoid the issue. Fixes pybind#1251
The stored vector of pybind-registered-type patients can grow without bound if a function is called with the same patient multiple times. This commit changes the pybind internal patient storage to an `unordered_set` to avoid the issue. Fixes pybind#1251
This fixes pybind#1251 (patient vector grows without bounds) for the 2.2.2 branch by checking that the vector doesn't already have the given patient. This is a little less elegant than the same fix for `master` (which changes the patients `vector` to an `unordered_set`), but that requires an internals layout change, which this approach avoids.
Awesome, thank you! |
This fixes #1251 (patient vector grows without bounds) for the 2.2.2 branch by checking that the vector doesn't already have the given patient. This is a little less elegant than the same fix for `master` (which changes the patients `vector` to an `unordered_set`), but that requires an internals layout change, which this approach avoids.
This looks resolved. Can we close this? |
Actually, this doesn't seem resolved, when I try the given code:
|
Huh... Can I ask which commit you were checking this code on? Does the unittest need to be fixed? |
@EricCousineau-TRI I'm on the current I also updated the code to make sure I have the right pybind11 version, but that seems to be OK: #include <pybind11/pybind11.h>
namespace py = pybind11;
struct Foo {};
#define XSTR(s) STR(s)
#define STR(s) #s
PYBIND11_MODULE(example, m) {
m.def("f", [](py::object o1, py::object o2) {}, py::keep_alive<1, 2>());
py::class_<Foo>(m, "Foo")
.def(py::init<>())
;
m.def("patients", [](py::object nurse) {
auto &internals = py::detail::get_internals();
py::list ret;
for (auto p : internals.patients[nurse.ptr()]) {
ret.append(py::reinterpret_borrow<py::object>(p));
}
return ret;
});
py::print(XSTR(PYBIND11_VERSION_MAJOR), XSTR(PYBIND11_VERSION_MINOR), XSTR(PYBIND11_VERSION_PATCH));
} $ python3.8
Python 3.8.0 (default, Oct 28 2019, 16:14:01)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from example import *
2 5 dev1
>>> nurse = Foo()
>>> p1 = Food()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'Food' is not defined
>>> p1 = Foo()
>>> nurse, p1
(<example.Foo object at 0x7f7459e878b0>, <example.Foo object at 0x7f7459e87eb0>)
>>> f(nurse, p1)
>>> patients(nurse)
[<example.Foo object at 0x7f7459e87eb0>]
>>> f(nurse, p1)
>>> patients(nurse)
[<example.Foo object at 0x7f7459e87eb0>, <example.Foo object at 0x7f7459e87eb0>]
>>> f(nurse, p1)
>>> f(nurse, p1)
>>> f(nurse, p1)
>>> patients(nurse)
[<example.Foo object at 0x7f7459e87eb0>, <example.Foo object at 0x7f7459e87eb0>, <example.Foo object at 0x7f7459e87eb0>, <example.Foo object at 0x7f7459e87eb0>, <example.Foo object at 0x7f7459e87eb0>]
>>> |
Gotcha, thanks! FTR @wjakob commented on PR #1253 10 days ago, but Jason may be busy so I'll see if I can update the PR / make a replacement PR. |
The stored vector of pybind-registered-type patients can grow without bound if a function is called with the same patient multiple times. This commit changes the pybind internal patient storage to an `unordered_set` to avoid the issue. Fixes pybind#1251
Ah, thanks for checking. I hadn't realized this was the cause! Would be great to get this in for some future release. But once again, this would break ABI, so it would need to be a 2.6 release? Can we meanwhile cherry-pick the v2.2 changes onto |
This fixes pybind#1251 (patient vector grows without bounds) for the 2.2.2 branch by checking that the vector doesn't already have the given patient. This is a little less elegant than the same fix for `master` in pybind#1253 (which changes the patients `vector` to an `unordered_set`), but that requires an internals layout change, which this approach avoids.
Probably! There is the I will defer to @wjakob on merging since it has downstream implications that you mentioned.
Overall, my pref would be that we take #1253 on wholesale onto My understanding is that yes, it's ABI-incompatible, but given the However, if you're intermingling bleeding-edge version of FTR I've filed #2289 as the forward-port, though. I realized the time it took me to type this out was about 3x longer than filing the cherry-pick :P |
I am currently crunching to prepare a keynote talk for a conference next week (Wednesday), I'll take a look after that. |
Sounds great, thank you!!! |
Will do!
No, but it might matter for determining what's the next increment in version number? At any rate, I don't have too strong opinions, but if I read this correctly, this was the reason there were these two separate PRs 2 years ago? (Of which unfortunately only one got merged. Maybe we should have an issue that keeps track of which PRs to merge for the next minor version increase?) |
True! And yeah, I too am ambivalent, but mainly b/c I'm not sure of the full implications. I will wait for Wenzel's feedback on the ABI-breaking PR after Wed.
Perhaps we can use GitHub's Milestones feature? |
That's what I meant. I just couldn't think of how it was called again :-) I'd be up for that, yes; worth discussing. |
Looking at some of the internals, it looks like
return_value_policy::reference_internal
is only "activated" when an instance iscreatednewly registered:https://github.com/pybind/pybind11/blob/48e1f9a/include/pybind11/cast.h#L552
However,
py::keep_alive<N, P>()
looks like it is always activated:https://github.com/pybind/pybind11/blob/48e1f9a/include/pybind11/pybind11.h#L140
https://github.com/pybind/pybind11/blob/48e1f9a/include/pybind11/attr.h#L442
https://github.com/pybind/pybind11/blob/48e1f9a/include/pybind11/pybind11.h#L1470
https://github.com/pybind/pybind11/blob/48e1f9a/include/pybind11/detail/class.h#L289
A simple fix (for only
pybind
-registered objects) would be to add a simple check:This would impact performance a little, but would prevent a packrat problem for long(er)-running Python programs.
Not sure about the non-
pybind
case inkeep_alive_impl
; however, it looks like this would just leak some reference counting, which may be less of an issue than an array growing.The text was updated successfully, but these errors were encountered: