From 61dc5dd38299552ba28e1872c411f9b5da5883aa Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Wed, 8 Jul 2026 21:27:32 +0900 Subject: [PATCH] [3.13] gh-153083: Defer GC tracking of an array to the end of construction. (gh-153284) (cherry picked from commit 8615062fd580934fc431746f64b1ac7ee6ef8e88) Co-authored-by: Donghee Na --- Include/internal/pycore_object.h | 3 +- ...-07-08-01-47-15.gh-issue-153083.XbKZCp.rst | 2 + Modules/arraymodule.c | 42 ++++++++++++++----- 3 files changed, 35 insertions(+), 12 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-07-08-01-47-15.gh-issue-153083.XbKZCp.rst diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h index 5877d43f4fd5a4d..cb72528807a31e6 100644 --- a/Include/internal/pycore_object.h +++ b/Include/internal/pycore_object.h @@ -766,7 +766,8 @@ static inline int _PyType_SUPPORTS_WEAKREFS(PyTypeObject *type) { return (type->tp_weaklistoffset != 0); } -extern PyObject* _PyType_AllocNoTrack(PyTypeObject *type, Py_ssize_t nitems); +// Export for 'array' shared extension. +PyAPI_FUNC(PyObject*) _PyType_AllocNoTrack(PyTypeObject *type, Py_ssize_t nitems); extern PyObject *_PyType_NewManagedObject(PyTypeObject *type); extern PyTypeObject* _PyType_CalculateMetaclass(PyTypeObject *, PyObject *); diff --git a/Misc/NEWS.d/next/Library/2026-07-08-01-47-15.gh-issue-153083.XbKZCp.rst b/Misc/NEWS.d/next/Library/2026-07-08-01-47-15.gh-issue-153083.XbKZCp.rst new file mode 100644 index 000000000000000..b6d96851e17342f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-07-08-01-47-15.gh-issue-153083.XbKZCp.rst @@ -0,0 +1,2 @@ +Defer GC tracking of an :class:`array.array` to the end of its construction. +Patch by Donghee Na. diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c index 8833ea2c0bf0f29..8e587af7169036e 100644 --- a/Modules/arraymodule.c +++ b/Modules/arraymodule.c @@ -13,6 +13,7 @@ #include "pycore_ceval.h" // _PyEval_GetBuiltin() #include "pycore_modsupport.h" // _PyArg_NoKeywords() #include "pycore_moduleobject.h" // _PyModule_GetState() +#include "pycore_object.h" // _PyObject_GC_TRACK() #include "pycore_weakref.h" // FT_CLEAR_WEAKREFS() #include // offsetof() @@ -677,7 +678,8 @@ class array.array "arrayobject *" "ArrayType" /*[clinic end generated code: output=da39a3ee5e6b4b0d input=a5c29edf59f176a3]*/ static PyObject * -newarrayobject(PyTypeObject *type, Py_ssize_t size, const struct arraydescr *descr) +newarrayobject_untracked(PyTypeObject *type, Py_ssize_t size, + const struct arraydescr *descr) { arrayobject *op; size_t nbytes; @@ -692,7 +694,7 @@ newarrayobject(PyTypeObject *type, Py_ssize_t size, const struct arraydescr *des return PyErr_NoMemory(); } nbytes = size * descr->itemsize; - op = (arrayobject *) type->tp_alloc(type, 0); + op = (arrayobject *) _PyType_AllocNoTrack(type, 0); if (op == NULL) { return NULL; } @@ -714,6 +716,16 @@ newarrayobject(PyTypeObject *type, Py_ssize_t size, const struct arraydescr *des return (PyObject *) op; } +static PyObject * +newarrayobject(PyTypeObject *type, Py_ssize_t size, const struct arraydescr *descr) +{ + PyObject *op = newarrayobject_untracked(type, size, descr); + if (op != NULL) { + _PyObject_GC_TRACK(op); + } + return op; +} + static PyObject * getarrayitem(PyObject *op, Py_ssize_t i) { @@ -921,13 +933,14 @@ array_slice(arrayobject *a, Py_ssize_t ilow, Py_ssize_t ihigh) ihigh = ilow; else if (ihigh > Py_SIZE(a)) ihigh = Py_SIZE(a); - np = (arrayobject *) newarrayobject(state->ArrayType, ihigh - ilow, a->ob_descr); + np = (arrayobject *) newarrayobject_untracked(state->ArrayType, ihigh - ilow, a->ob_descr); if (np == NULL) return NULL; if (ihigh > ilow) { memcpy(np->ob_item, a->ob_item + ilow * a->ob_descr->itemsize, (ihigh-ilow) * a->ob_descr->itemsize); } + _PyObject_GC_TRACK(np); return (PyObject *)np; } @@ -997,7 +1010,7 @@ array_concat(arrayobject *a, PyObject *bb) return PyErr_NoMemory(); } size = Py_SIZE(a) + Py_SIZE(b); - np = (arrayobject *) newarrayobject(state->ArrayType, size, a->ob_descr); + np = (arrayobject *) newarrayobject_untracked(state->ArrayType, size, a->ob_descr); if (np == NULL) { return NULL; } @@ -1008,6 +1021,7 @@ array_concat(arrayobject *a, PyObject *bb) memcpy(np->ob_item + Py_SIZE(a)*a->ob_descr->itemsize, b->ob_item, Py_SIZE(b)*b->ob_descr->itemsize); } + _PyObject_GC_TRACK(np); return (PyObject *)np; #undef b } @@ -1024,16 +1038,19 @@ array_repeat(arrayobject *a, Py_ssize_t n) return PyErr_NoMemory(); } Py_ssize_t size = array_length * n; - arrayobject* np = (arrayobject *) newarrayobject(state->ArrayType, size, a->ob_descr); + arrayobject* np = (arrayobject *) newarrayobject_untracked(state->ArrayType, size, a->ob_descr); if (np == NULL) return NULL; - if (size == 0) + if (size == 0) { + _PyObject_GC_TRACK(np); return (PyObject *)np; + } const Py_ssize_t oldbytes = array_length * a->ob_descr->itemsize; const Py_ssize_t newbytes = oldbytes * n; _PyBytes_Repeat(np->ob_item, newbytes, a->ob_item, oldbytes); + _PyObject_GC_TRACK(np); return (PyObject *)np; } @@ -2504,17 +2521,17 @@ array_subscr(arrayobject* self, PyObject* item) return newarrayobject(state->ArrayType, 0, self->ob_descr); } else if (step == 1) { - PyObject *result = newarrayobject(state->ArrayType, - slicelength, self->ob_descr); + PyObject *result = newarrayobject_untracked(state->ArrayType, slicelength, self->ob_descr); if (result == NULL) return NULL; memcpy(((arrayobject *)result)->ob_item, self->ob_item + start * itemsize, slicelength * itemsize); + _PyObject_GC_TRACK(result); return result; } else { - result = newarrayobject(state->ArrayType, slicelength, self->ob_descr); + result = newarrayobject_untracked(state->ArrayType, slicelength, self->ob_descr); if (!result) return NULL; ar = (arrayobject*)result; @@ -2526,6 +2543,7 @@ array_subscr(arrayobject* self, PyObject* item) itemsize); } + _PyObject_GC_TRACK(result); return result; } } @@ -2822,7 +2840,7 @@ array_new(PyTypeObject *type, PyObject *args, PyObject *kwds) else len = 0; - a = newarrayobject(type, len, descr); + a = newarrayobject_untracked(type, len, descr); if (a == NULL) { Py_XDECREF(it); return NULL; @@ -2911,6 +2929,8 @@ array_new(PyTypeObject *type, PyObject *args, PyObject *kwds) } Py_DECREF(it); } + // Track only once fully built. + _PyObject_GC_TRACK(a); return a; } } @@ -3003,7 +3023,7 @@ static PyType_Slot array_slots[] = { {Py_tp_methods, array_methods}, {Py_tp_members, array_members}, {Py_tp_getset, array_getsets}, - {Py_tp_alloc, PyType_GenericAlloc}, + {Py_tp_alloc, _PyType_AllocNoTrack}, {Py_tp_new, array_new}, {Py_tp_traverse, array_tp_traverse},