-
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
[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. |
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.
Suggestions aiming to nudge things to the right places.
include/pybind11/pybind11.h
Outdated
@@ -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 |
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 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code for the erasure was actually copied and modified from
pybind11/include/pybind11/detail/class.h
Line 218 in e450eb6
// Actually just `std::erase_if`, but that's only available in C++20 |
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.
tests/test_class.cpp
Outdated
test_derived(test_derived const &Copy) = delete; | ||
}; | ||
|
||
class py_test_derived : public test_derived { |
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.
Personal preference for clarity (in case you like that, too): class test_derived_trampoline : test_derived
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.
Renamed to test_override_cache_helper_trampoline
tests/test_class.cpp
Outdated
@@ -34,6 +34,24 @@ struct NoBraceInitialization { | |||
std::vector<int> vec; | |||
}; | |||
|
|||
class test_derived { |
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved and renamed to test_override_cache_helper
tests/test_embed/test_derived.py
Outdated
@@ -0,0 +1,18 @@ | |||
# -*- coding: utf-8 -*- |
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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? |
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.
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: