[master] Wrong caching of overrides - #3465
Conversation
There was a problem when the python type, which was stored in override cache for C++ functions, was destroyed and the record wasn't removed from the override cache. Therefor, dangling pointer was stored there. Then when the memory was reused and new type was allocated at the given address and the method with the same name (as previously stored in the cache) was actually overridden in python, it would wrongly find it in the override cache for C++ functions and therefor override from python wouldn't be called. The fix is to erase the type from the override cache when the type is destroyed.
|
FYI I'll be on and off a lot today but will keep an eye on this. |
rwgk
left a comment
There was a problem hiding this comment.
Suggestions aiming to nudge things to the right places.
| weakref((PyObject *) type, cpp_function([type](handle wr) { | ||
| get_internals().registered_types_py.erase(type); | ||
|
|
||
| // Actually just `std::erase_if`, but that's only available in C++20 |
There was a problem hiding this comment.
What do you think about something like:
erase_if(cache.begin(), cache.end(), [](const xxx& v){ return yyy; });
here and implementing erase_if in detail/common.h (bottom), using the "(2) equivalent to" code from here:
https://en.cppreference.com/w/cpp/container/vector/erase2
auto it = std::remove_if(c.begin(), c.end(), pred);
auto r = std::distance(it, c.end());
c.erase(it, c.end());
return r;
Later, when we introduce PYBIND11_CPP20 we can use std::erase_if directly. (Maybe just leave something like // TODO PYBIND11_CPP20: std::erase_if for now.)
There was a problem hiding this comment.
The code for the erasure was actually copied and modified from
pybind11/include/pybind11/detail/class.h
Line 218 in e450eb6
It would be better to create a helper function as you suggested, but I didn't want to add it in this PR as it isn't isn't connected to the issue. I think it would be better to add it as a separate commit.
| test_derived(test_derived const &Copy) = delete; | ||
| }; | ||
|
|
||
| class py_test_derived : public test_derived { |
There was a problem hiding this comment.
Personal preference for clarity (in case you like that, too): class test_derived_trampoline : test_derived
There was a problem hiding this comment.
Renamed to test_override_cache_helper_trampoline
| std::vector<int> vec; | ||
| }; | ||
|
|
||
| class test_derived { |
There was a problem hiding this comment.
At 2nd look, test_virtual_functions.* appears to be a significantly better fit, because this is closely tied to trampolines.
test_derived is a very generic name, something a bit more tailored to the purpose would be ideal (easier to pin-point and relate to what's being exerised). Maybe exercise_override_cache or test_override_cache_helper ...?
There was a problem hiding this comment.
Moved and renamed to test_override_cache_helper
| @@ -0,0 +1,18 @@ | |||
| # -*- coding: utf-8 -*- | |||
There was a problem hiding this comment.
Does test_trampoline make sense here? To leave it a little bit open (for the future) but not a widely as "derived".
There was a problem hiding this comment.
Renamed to test_trampoline.py
Rename the classes and files so they're no too generic. Also, better place to test the stuff is in test_virtual_functions.cpp/.py as we're basically testing the virtual functions/trampolines.
|
I cannot look at the code right now, but from the comments it sounds good.
If you haven't done already, could you please add a comment here: todo
consolidate with xxx in class.h?
Also request review from Skylion007
…On Sat, Nov 13, 2021 at 10:12 Trigve ***@***.***> wrote:
***@***.**** commented on this pull request.
------------------------------
In include/pybind11/pybind11.h
<#3465 (comment)>:
> @@ -1979,6 +1979,16 @@ inline std::pair<decltype(internals::registered_types_py)::iterator, bool> all_t
// gets destroyed:
weakref((PyObject *) type, cpp_function([type](handle wr) {
get_internals().registered_types_py.erase(type);
+
+ // Actually just `std::erase_if`, but that's only available in C++20
The code for the erasure was actually copied and modified from
https://github.com/pybind/pybind11/blob/e450eb62c21c8518c1988c3b17bc5793ea347756/include/pybind11/detail/class.h#L218
It would be better to create a helper function as you suggested, but I
didn't want to add it in this PR as it isn't isn't connected to the issue.
I think it would be better to add it as a separate commit.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#3465 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAFUZAA6P4EP245EVCQQSSLUL2TCFANCNFSM5H6ZWIQQ>
.
Triage notifications on the go with GitHub Mobile for iOS
<https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675>
or Android
<https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>.
|
for more information, see https://pre-commit.ci
How do I request a review? I've read the docs but I don't have option to add one. |
|
Upper right corner in the main PR view is where I'd expect it. (I can add
Aaron later if it's not there for you.)
…On Sat, Nov 13, 2021 at 11:02 Trigve ***@***.***> wrote:
I cannot look at the code right now, but from the comments it sounds good.
If you haven't done already, could you please add a comment here: todo
consolidate with xxx in class.h? Also request review from Skylion007
How do I request a review? I've read the docs but I don't have option to
add one.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#3465 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAFUZAG6OFRYA3TQ46OK4NTUL2Y5FANCNFSM5H6ZWIQQ>
.
Triage notifications on the go with GitHub Mobile for iOS
<https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675>
or Android
<https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>.
|
|
@henryiii do you want to take a look, too? |
henryiii
left a comment
There was a problem hiding this comment.
Looks reasonable to me.
|
Thanks @Trigve for the great unit tests! |
Description
There was a problem when the python type, which was stored in override
cache for C++ functions, was destroyed and the record wasn't removed from the
override cache. Therefor, dangling pointer was stored there. Then when the
memory was reused and new type was allocated at the given address and the
method with the same name (as previously stored in the cache) was actually
overridden in python, it would wrongly find it in the override cache for C++
functions and therefor override from python wouldn't be called.
The fix is to erase the type from the override cache when the type is destroyed.
Closes: #3369
Suggested changelog entry: