-
-
Notifications
You must be signed in to change notification settings - Fork 31.9k
GH-81061: Fix refcount issue when returning None
from a ctypes.py_object
callback
#13364
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
GH-81061: Fix refcount issue when returning None
from a ctypes.py_object
callback
#13364
Conversation
Thank you to Eryk Sun for finding the cause of the bug and providing the code fix.
Previously, our ctypes_patch would trigger a bug in ctypes (see https://bugs.python.org/issue36880 and python/cpython#13364), which would eventually crash Python if done too often. This workaround changes our ctypes_patch implementation so that it no longer triggers the ctypes bug. This fixes the crash reported in beeware/toga#549.
I don't feel competent to review ctypes issues, sorry. Perhaps @serhiy-storchaka |
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.
There are merge conflicts.
A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated. Once you have made the requested changes, please leave a comment on this pull request containing the phrase |
# Conflicts: # Modules/_ctypes/callbacks.c
Fixed the conflict, thanks. I have made the requested changes; please review again. |
Thanks for making the requested changes! @isidentical, @kumaraditya303: please review the changes made to this pull request. |
@support.refcount_test | ||
def test_callback_py_object_none_return(self): |
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.
AFAICT, this test is rendered pointless in 3.12 due to the adoption of PEP 683 (Immortal Objects, Using a Fixed Refcount). Because None
is immortal, executing Py_INCREF(Py_None)
and Py_DECREF(Py_None)
will no longer modify its refcount.
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.
Ah, good to know. I assume this isn't implemented yet though, because as of the current state of main
(447d061), this test still hard-crashes without the fix.
# Allow for small variations in None's refcount from other | ||
# sources. | ||
self.assertAlmostEqual( | ||
sys.getrefcount(None), none_refcount, delta=50) |
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.
Yeah, in general we don't do these kind of tests. We have an automatic refleak detection mechanism but it will only work if the path that leaks references is exercised in the test suite. This means that you can add a test that ensures that the code you are adding to callbacks.c
is actually executed but your test doesn't need to try to detect the refleak, that will be done automatically. I suppose the reason this was never detected is that we don't have a test that actually runs that.
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.
Ah okay. I was going by what the other tests in test_refcounts
are doing. Should I adjust those as well to not check sys.getrefcount
manually?
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.
Should I adjust those as well to not check
sys.getrefcount
manually?
Not as part of this issue (one issue per problem - only changing what's required for the problem at hand). I haven't checked those and maybe these are checked more specific things.
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.
You can confirm that your test work running `./python -m test test_ctypes -R'.
Ensure that it detects the problem without your fix first :)
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.
Thanks, I didn't know that just -m test
works too (I used -m test.regrtest
). Yes, I checked - with -R
it now indeed detects the original problem.
return None | ||
|
||
# Check that calling func does not affect None's refcount. | ||
for _ in range(10000): |
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.
Single call should be enough to trigger the refleak checker.
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.
Unfortunately not... I tested again - apparently the refleak checker (with default settings: -m test --huntrleaks=: test.test_ctypes.test_refcounts
) only detects if None
's refcount is too high, not if it's too low. The latter case is only detected once None
's refcount gets to zero/negative (which triggers an assertion), and that requires a thousand or so iterations of the broken code.
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.
If None
refcount goes too small it will crash at some point assuming the rest of the code decrements normally
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.
Unfortunately not... I tested again - apparently the refleak checker (with default settings: -m test --huntrleaks=: test.test_ctypes.test_refcounts) only detects if None's refcount is too high, not if it's too low. The latter case is only detected once None's refcount gets to zero/negative (which triggers an assertion), and that requires a thousand or so iterations of the broken code.
I see, it is because of #74959.
🤖 New build scheduled with the buildbot fleet by @kumaraditya303 for commit 66ea175 🤖 If you want to schedule another build, you need to add the |
None
from a ctypes.py_object
callback
Buildbots are happy, merging. Thanks for the PR. |
Thanks @dgelessus for the PR, and @kumaraditya303 for merging it 🌮🎉.. I'm working now to backport this PR to: 3.10, 3.11. |
GH-100877 is a backport of this pull request to the 3.11 branch. |
GH-100878 is a backport of this pull request to the 3.10 branch. |
…es.py_object` callback (pythonGH-13364) (cherry picked from commit 837ba05) Co-authored-by: dgelessus <[email protected]>
…es.py_object` callback (pythonGH-13364) (cherry picked from commit 837ba05) Co-authored-by: dgelessus <[email protected]>
…object` callback (GH-13364) (cherry picked from commit 837ba05) Co-authored-by: dgelessus <[email protected]>
…object` callback (GH-13364) (cherry picked from commit 837ba05) Co-authored-by: dgelessus <[email protected]>
Right now this only adds a failing test for the issue, to check that the bug is properly detected in CI. Once that runs through I'll push the commit with the actual fix.Donehttps://bugs.python.org/issue36880