-
-
Notifications
You must be signed in to change notification settings - Fork 35.3k
GH-155728: Untrack tuples while unmarshalling #156423
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,5 @@ | ||
| import gc | ||
|
|
||
| from test import support | ||
| from test.support import is_apple_mobile, os_helper, requires_debug_ranges, is_emscripten | ||
| from test.support.script_helper import assert_python_ok | ||
|
|
@@ -872,6 +874,55 @@ def test_read_object_from_file(self): | |
| _testcapi.pymarshal_read_object_from_file(os_helper.TESTFN) | ||
| os_helper.unlink(os_helper.TESTFN) | ||
|
|
||
| @support.cpython_only | ||
| class GCTrackingTestCase(unittest.TestCase): | ||
|
|
||
| def _not_tracked_instantly(self, t): | ||
| new = marshal.loads(marshal.dumps(t)) | ||
|
|
||
| self.assertFalse(gc.is_tracked(t), t) | ||
| self.assertFalse(gc.is_tracked(new), new) | ||
|
|
||
| def _not_tracked(self, t): | ||
| # Nested tuples can take several collections to untrack | ||
| gc.collect() | ||
| gc.collect() | ||
|
|
||
| new = marshal.loads(marshal.dumps(t)) | ||
|
|
||
| self.assertFalse(gc.is_tracked(t), t) | ||
| self.assertFalse(gc.is_tracked(new), new) | ||
|
|
||
| def _tracked(self, t): | ||
| new = marshal.loads(marshal.dumps(t)) | ||
|
|
||
| self.assertTrue(gc.is_tracked(t), t) | ||
| self.assertTrue(gc.is_tracked(new), new) | ||
|
|
||
| def testTuple(self): | ||
| x, y, z = 1.5, "a", [] | ||
|
|
||
| self._not_tracked_instantly(()) | ||
| self._not_tracked_instantly((1,)) | ||
| self._not_tracked_instantly((1, 2)) | ||
| self._not_tracked_instantly((1, 2, "a")) | ||
| self._not_tracked_instantly((12, 10**10, 'a_' * 100)) | ||
|
|
||
| # Test for _PyTuple_Concat | ||
| self._not_tracked_instantly((1, 2) + (2, 3)) | ||
|
|
||
| # Test for _PyTuple_Repeat | ||
| self._not_tracked_instantly((1, 2) * 5) | ||
|
|
||
| self._not_tracked(((1, x), y, (2, 3))) | ||
| self._not_tracked((1, 2, (True, False, ()))) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe, something like this? Because for now |
||
|
|
||
| self._tracked(([],)) | ||
| self._tracked(([1],)) | ||
| self._tracked(({},)) | ||
| self._tracked((set(),)) | ||
| self._tracked((x, y, z)) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| unittest.main() | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| Untrack tuples from the GC when possible during unmarshalling. Patch by | ||
| Sergey Miryanov. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1180,6 +1180,7 @@ r_object(RFILE *p) | |
| int type, code = r_byte(p); | ||
| int flag, is_interned = 0; | ||
| PyObject *retval = NULL; | ||
| bool track_tuple = false; | ||
|
|
||
| if (code == EOF) { | ||
| if (PyErr_ExceptionMatches(PyExc_EOFError)) { | ||
|
|
@@ -1422,16 +1423,28 @@ r_object(RFILE *p) | |
| if (v == NULL) | ||
| break; | ||
|
|
||
| // empty tuples are untracked, and we can check if n > 0, | ||
| // but using PyObject_GC_UnTrack is clearer | ||
| PyObject_GC_UnTrack(v); | ||
| track_tuple = false; | ||
|
|
||
| for (i = 0; i < n; i++) { | ||
| v2 = r_object(p); | ||
| if ( v2 == NULL ) { | ||
| if (!PyErr_Occurred()) | ||
| PyErr_SetString(PyExc_TypeError, | ||
| "NULL object in marshal data for tuple"); | ||
| Py_SETREF(v, NULL); | ||
| track_tuple = false; | ||
| break; | ||
| } | ||
| PyTuple_SET_ITEM(v, i, v2); | ||
| if (!track_tuple && PyObject_GC_IsTracked(v2)) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| track_tuple = true; | ||
| } | ||
| } | ||
| if (track_tuple) { | ||
| _PyObject_GC_TRACK(v); | ||
| } | ||
| retval = v; | ||
| break; | ||
|
|
||
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.
Maybe we should move this import below?