From 8ef6e98391e084fb6124c762a055246d9b167c26 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Tue, 25 May 2021 21:35:39 +0200 Subject: [PATCH 01/11] bpo-42972: Fully implement GC protocol for functools types --- Modules/_functoolsmodule.c | 95 ++++++++++++++++++++++++++------------ 1 file changed, 65 insertions(+), 30 deletions(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 19cfa9b07b340d..f5da80b2480e2c 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -147,18 +147,37 @@ partial_new(PyTypeObject *type, PyObject *args, PyObject *kw) return (PyObject *)pto; } +static int +partial_clear(partialobject *pto) +{ + Py_CLEAR(pto->fn); + Py_CLEAR(pto->args); + Py_CLEAR(pto->kw); + Py_CLEAR(pto->dict); + return 0; +} + +static int +partial_traverse(partialobject *pto, visitproc visit, void *arg) +{ + Py_VISIT(pto->fn); + Py_VISIT(pto->args); + Py_VISIT(pto->kw); + Py_VISIT(pto->dict); + Py_VISIT(Py_TYPE(pto)); + return 0; +} + static void partial_dealloc(partialobject *pto) { PyTypeObject *tp = Py_TYPE(pto); /* bpo-31095: UnTrack is needed before calling any callbacks */ PyObject_GC_UnTrack(pto); - if (pto->weakreflist != NULL) + if (pto->weakreflist != NULL) { PyObject_ClearWeakRefs((PyObject *) pto); - Py_XDECREF(pto->fn); - Py_XDECREF(pto->args); - Py_XDECREF(pto->kw); - Py_XDECREF(pto->dict); + } + (void)partial_clear(pto); tp->tp_free(pto); Py_DECREF(tp); } @@ -307,16 +326,6 @@ partial_call(partialobject *pto, PyObject *args, PyObject *kwargs) return res; } -static int -partial_traverse(partialobject *pto, visitproc visit, void *arg) -{ - Py_VISIT(pto->fn); - Py_VISIT(pto->args); - Py_VISIT(pto->kw); - Py_VISIT(pto->dict); - return 0; -} - PyDoc_STRVAR(partial_doc, "partial(func, *args, **keywords) - new function with partial application\n\ of the given arguments and keywords.\n"); @@ -469,11 +478,11 @@ static PyType_Slot partial_type_slots[] = { {Py_tp_setattro, PyObject_GenericSetAttr}, {Py_tp_doc, (void *)partial_doc}, {Py_tp_traverse, partial_traverse}, + {Py_tp_clear, partial_clear}, {Py_tp_methods, partial_methods}, {Py_tp_members, partial_memberlist}, {Py_tp_getset, partial_getsetlist}, {Py_tp_new, partial_new}, - {Py_tp_free, PyObject_GC_Del}, {0, 0} }; @@ -506,8 +515,9 @@ static void keyobject_dealloc(keyobject *ko) { PyTypeObject *tp = Py_TYPE(ko); - keyobject_clear(ko); - PyObject_Free(ko); + PyObject_GC_UnTrack(ko); + (void)keyobject_clear(ko); + PyObject_GC_Del(ko); Py_DECREF(tp); } @@ -516,6 +526,7 @@ keyobject_traverse(keyobject *ko, visitproc visit, void *arg) { Py_VISIT(ko->cmp); Py_VISIT(ko->object); + Py_VISIT(Py_TYPE(ko)); return 0; } @@ -546,7 +557,8 @@ static PyType_Slot keyobject_type_slots[] = { static PyType_Spec keyobject_type_spec = { .name = "functools.KeyWrapper", .basicsize = sizeof(keyobject), - .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION, + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | + Py_TPFLAGS_HAVE_GC), .slots = keyobject_type_slots }; @@ -560,7 +572,7 @@ keyobject_call(keyobject *ko, PyObject *args, PyObject *kwds) if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:K", kwargs, &object)) return NULL; - result = PyObject_New(keyobject, Py_TYPE(ko)); + result = PyObject_GC_New(keyobject, Py_TYPE(ko)); if (result == NULL) { return NULL; } @@ -568,6 +580,7 @@ keyobject_call(keyobject *ko, PyObject *args, PyObject *kwds) result->cmp = ko->cmp; Py_INCREF(object); result->object = object; + PyObject_GC_Track(result); return (PyObject *)result; } @@ -621,12 +634,13 @@ functools_cmp_to_key(PyObject *self, PyObject *args, PyObject *kwds) return NULL; state = get_functools_state(self); - object = PyObject_New(keyobject, state->keyobject_type); + object = PyObject_GC_New(keyobject, state->keyobject_type); if (!object) return NULL; Py_INCREF(cmp); object->cmp = cmp; object->object = NULL; + PyObject_GC_Track(object); return (PyObject *)object; } @@ -748,25 +762,45 @@ typedef struct lru_list_elem { PyObject *key, *result; } lru_list_elem; +static int +lru_list_elem_clear(lru_list_elem *link) +{ + Py_CLEAR(link->key); + Py_CLEAR(link->result); + return 0; +} + +static int +lru_list_elem_traverse(lru_list_elem *link, visitproc visit, void *arg) +{ + Py_VISIT(link->key); + Py_VISIT(link->result); + Py_VISIT(Py_TYPE(link)); + return 0; +} + static void lru_list_elem_dealloc(lru_list_elem *link) { PyTypeObject *tp = Py_TYPE(link); - Py_XDECREF(link->key); - Py_XDECREF(link->result); - PyObject_Free(link); + PyObject_GC_UnTrack(link); + (void)lru_list_elem_clear(link); + PyObject_GC_Del(link); Py_DECREF(tp); } static PyType_Slot lru_list_elem_type_slots[] = { {Py_tp_dealloc, lru_list_elem_dealloc}, + {Py_tp_traverse, lru_list_elem_traverse}, + {Py_tp_clear, lru_list_elem_clear}, {0, 0} }; static PyType_Spec lru_list_elem_type_spec = { .name = "functools._lru_list_elem", .basicsize = sizeof(lru_list_elem), - .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION, + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | + Py_TPFLAGS_HAVE_GC), .slots = lru_list_elem_type_slots }; @@ -1031,8 +1065,8 @@ bounded_lru_cache_wrapper(lru_cache_object *self, PyObject *args, PyObject *kwds self->root.next == &self->root) { /* Cache is not full, so put the result in a new link */ - link = (lru_list_elem *)PyObject_New(lru_list_elem, - self->lru_list_elem_type); + link = (lru_list_elem *)PyObject_GC_New(lru_list_elem, + self->lru_list_elem_type); if (link == NULL) { Py_DECREF(key); Py_DECREF(result); @@ -1042,6 +1076,7 @@ bounded_lru_cache_wrapper(lru_cache_object *self, PyObject *args, PyObject *kwds link->hash = hash; link->key = key; link->result = result; + PyObject_GC_Track(link); /* What is really needed here is a SetItem variant with a "no clobber" option. If the __eq__ call triggers a reentrant call that adds this same key, then this setitem call will update the cache dict @@ -1260,7 +1295,7 @@ lru_cache_dealloc(lru_cache_object *obj) PyObject_ClearWeakRefs((PyObject*)obj); } - lru_cache_tp_clear(obj); + (void)lru_cache_tp_clear(obj); tp->tp_free(obj); Py_DECREF(tp); } @@ -1330,8 +1365,7 @@ lru_cache_tp_traverse(lru_cache_object *self, visitproc visit, void *arg) lru_list_elem *link = self->root.next; while (link != &self->root) { lru_list_elem *next = link->next; - Py_VISIT(link->key); - Py_VISIT(link->result); + Py_VISIT(link); link = next; } Py_VISIT(self->func); @@ -1340,6 +1374,7 @@ lru_cache_tp_traverse(lru_cache_object *self, visitproc visit, void *arg) Py_VISIT(self->lru_list_elem_type); Py_VISIT(self->cache_info_type); Py_VISIT(self->dict); + Py_VISIT(Py_TYPE(self)); return 0; } From 6d7cdaf5d07853711b444ba755250ddd23e359a9 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 26 May 2021 20:53:55 +0200 Subject: [PATCH 02/11] Address review --- Modules/_functoolsmodule.c | 36 ++++++++---------------------------- 1 file changed, 8 insertions(+), 28 deletions(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index f5da80b2480e2c..448102a1a0341b 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -762,45 +762,25 @@ typedef struct lru_list_elem { PyObject *key, *result; } lru_list_elem; -static int -lru_list_elem_clear(lru_list_elem *link) -{ - Py_CLEAR(link->key); - Py_CLEAR(link->result); - return 0; -} - -static int -lru_list_elem_traverse(lru_list_elem *link, visitproc visit, void *arg) -{ - Py_VISIT(link->key); - Py_VISIT(link->result); - Py_VISIT(Py_TYPE(link)); - return 0; -} - static void lru_list_elem_dealloc(lru_list_elem *link) { PyTypeObject *tp = Py_TYPE(link); - PyObject_GC_UnTrack(link); - (void)lru_list_elem_clear(link); - PyObject_GC_Del(link); + Py_CLEAR(link->key); + Py_CLEAR(link->result); + tp->tp_free(link); Py_DECREF(tp); } static PyType_Slot lru_list_elem_type_slots[] = { {Py_tp_dealloc, lru_list_elem_dealloc}, - {Py_tp_traverse, lru_list_elem_traverse}, - {Py_tp_clear, lru_list_elem_clear}, {0, 0} }; static PyType_Spec lru_list_elem_type_spec = { .name = "functools._lru_list_elem", .basicsize = sizeof(lru_list_elem), - .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | - Py_TPFLAGS_HAVE_GC), + .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION, .slots = lru_list_elem_type_slots }; @@ -1065,8 +1045,8 @@ bounded_lru_cache_wrapper(lru_cache_object *self, PyObject *args, PyObject *kwds self->root.next == &self->root) { /* Cache is not full, so put the result in a new link */ - link = (lru_list_elem *)PyObject_GC_New(lru_list_elem, - self->lru_list_elem_type); + link = (lru_list_elem *)PyObject_New(lru_list_elem, + self->lru_list_elem_type); if (link == NULL) { Py_DECREF(key); Py_DECREF(result); @@ -1076,7 +1056,6 @@ bounded_lru_cache_wrapper(lru_cache_object *self, PyObject *args, PyObject *kwds link->hash = hash; link->key = key; link->result = result; - PyObject_GC_Track(link); /* What is really needed here is a SetItem variant with a "no clobber" option. If the __eq__ call triggers a reentrant call that adds this same key, then this setitem call will update the cache dict @@ -1365,7 +1344,8 @@ lru_cache_tp_traverse(lru_cache_object *self, visitproc visit, void *arg) lru_list_elem *link = self->root.next; while (link != &self->root) { lru_list_elem *next = link->next; - Py_VISIT(link); + Py_VISIT(link->key); + Py_VISIT(link->result); link = next; } Py_VISIT(self->func); From a15b49fedbb71ddd9c30a4024d3235f7e91fdece Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Thu, 27 May 2021 11:40:25 +0200 Subject: [PATCH 03/11] Address review: visit node type for each strong ref --- Modules/_functoolsmodule.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 448102a1a0341b..862976270c8616 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -1346,6 +1346,7 @@ lru_cache_tp_traverse(lru_cache_object *self, visitproc visit, void *arg) lru_list_elem *next = link->next; Py_VISIT(link->key); Py_VISIT(link->result); + Py_VISIT(Py_TYPE(link)); link = next; } Py_VISIT(self->func); From 0c8df1f1741c6acf85d26b22d8e989d773ee4a3d Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Thu, 27 May 2021 19:37:17 +0200 Subject: [PATCH 04/11] Address review: visit members in correct order --- Modules/_functoolsmodule.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 862976270c8616..ec5e111134e6de 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -160,11 +160,11 @@ partial_clear(partialobject *pto) static int partial_traverse(partialobject *pto, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(pto)); Py_VISIT(pto->fn); Py_VISIT(pto->args); Py_VISIT(pto->kw); Py_VISIT(pto->dict); - Py_VISIT(Py_TYPE(pto)); return 0; } @@ -524,9 +524,9 @@ keyobject_dealloc(keyobject *ko) static int keyobject_traverse(keyobject *ko, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(ko)); Py_VISIT(ko->cmp); Py_VISIT(ko->object); - Py_VISIT(Py_TYPE(ko)); return 0; } @@ -1341,6 +1341,7 @@ lru_cache_deepcopy(PyObject *self, PyObject *unused) static int lru_cache_tp_traverse(lru_cache_object *self, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(self)); lru_list_elem *link = self->root.next; while (link != &self->root) { lru_list_elem *next = link->next; @@ -1349,13 +1350,12 @@ lru_cache_tp_traverse(lru_cache_object *self, visitproc visit, void *arg) Py_VISIT(Py_TYPE(link)); link = next; } - Py_VISIT(self->func); Py_VISIT(self->cache); + Py_VISIT(self->func); Py_VISIT(self->kwd_mark); Py_VISIT(self->lru_list_elem_type); Py_VISIT(self->cache_info_type); Py_VISIT(self->dict); - Py_VISIT(Py_TYPE(self)); return 0; } From f9ac4c5cbae43fa69299c64af2c4c11eec2d457e Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Thu, 27 May 2021 20:18:13 +0200 Subject: [PATCH 05/11] Address review: revert Py_CLEAR change in lru_list_elem_dealloc --- Modules/_functoolsmodule.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index ec5e111134e6de..976ed287232eee 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -766,8 +766,8 @@ static void lru_list_elem_dealloc(lru_list_elem *link) { PyTypeObject *tp = Py_TYPE(link); - Py_CLEAR(link->key); - Py_CLEAR(link->result); + Py_XDECREF(link->key); + Py_XDECREF(link->result); tp->tp_free(link); Py_DECREF(tp); } From fc39917b8808c70553ac4606154dfb628b559134 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Thu, 27 May 2021 20:22:22 +0200 Subject: [PATCH 06/11] Revert LRU cache element optimisation This reverts commit 6d7cdaf5d07853711b444ba755250ddd23e359a9. --- Modules/_functoolsmodule.c | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 976ed287232eee..3201c5b5432599 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -762,10 +762,28 @@ typedef struct lru_list_elem { PyObject *key, *result; } lru_list_elem; +static int +lru_list_elem_clear(lru_list_elem *link) +{ + Py_CLEAR(link->key); + Py_CLEAR(link->result); + return 0; +} + +static int +lru_list_elem_traverse(lru_list_elem *link, visitproc visit, void *arg) +{ + Py_VISIT(link->key); + Py_VISIT(link->result); + Py_VISIT(Py_TYPE(link)); + return 0; +} + static void lru_list_elem_dealloc(lru_list_elem *link) { PyTypeObject *tp = Py_TYPE(link); + PyObject_GC_UnTrack(link); Py_XDECREF(link->key); Py_XDECREF(link->result); tp->tp_free(link); @@ -774,13 +792,16 @@ lru_list_elem_dealloc(lru_list_elem *link) static PyType_Slot lru_list_elem_type_slots[] = { {Py_tp_dealloc, lru_list_elem_dealloc}, + {Py_tp_traverse, lru_list_elem_traverse}, + {Py_tp_clear, lru_list_elem_clear}, {0, 0} }; static PyType_Spec lru_list_elem_type_spec = { .name = "functools._lru_list_elem", .basicsize = sizeof(lru_list_elem), - .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION, + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | + Py_TPFLAGS_HAVE_GC), .slots = lru_list_elem_type_slots }; @@ -1045,8 +1066,8 @@ bounded_lru_cache_wrapper(lru_cache_object *self, PyObject *args, PyObject *kwds self->root.next == &self->root) { /* Cache is not full, so put the result in a new link */ - link = (lru_list_elem *)PyObject_New(lru_list_elem, - self->lru_list_elem_type); + link = (lru_list_elem *)PyObject_GC_New(lru_list_elem, + self->lru_list_elem_type); if (link == NULL) { Py_DECREF(key); Py_DECREF(result); @@ -1056,6 +1077,7 @@ bounded_lru_cache_wrapper(lru_cache_object *self, PyObject *args, PyObject *kwds link->hash = hash; link->key = key; link->result = result; + PyObject_GC_Track(link); /* What is really needed here is a SetItem variant with a "no clobber" option. If the __eq__ call triggers a reentrant call that adds this same key, then this setitem call will update the cache dict @@ -1345,9 +1367,7 @@ lru_cache_tp_traverse(lru_cache_object *self, visitproc visit, void *arg) lru_list_elem *link = self->root.next; while (link != &self->root) { lru_list_elem *next = link->next; - Py_VISIT(link->key); - Py_VISIT(link->result); - Py_VISIT(Py_TYPE(link)); + Py_VISIT(link); link = next; } Py_VISIT(self->cache); From 07f21822f593195fe7367289425430e825255989 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Thu, 27 May 2021 20:24:41 +0200 Subject: [PATCH 07/11] Address review: revert redundant slot removal --- Modules/_functoolsmodule.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 3201c5b5432599..31bedee4181a21 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -483,6 +483,7 @@ static PyType_Slot partial_type_slots[] = { {Py_tp_members, partial_memberlist}, {Py_tp_getset, partial_getsetlist}, {Py_tp_new, partial_new}, + {Py_tp_free, PyObject_GC_Del}, {0, 0} }; From 0861788ddb266c7b18a375be720294132a6d56fe Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Thu, 27 May 2021 20:25:54 +0200 Subject: [PATCH 08/11] Address review: use tp_free --- Modules/_functoolsmodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 31bedee4181a21..7a908b77362571 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -518,7 +518,7 @@ keyobject_dealloc(keyobject *ko) PyTypeObject *tp = Py_TYPE(ko); PyObject_GC_UnTrack(ko); (void)keyobject_clear(ko); - PyObject_GC_Del(ko); + tp->tp_free(ko); Py_DECREF(tp); } From 7c2f750c3e61e52db82fedf527bdc49fc9c33c87 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Thu, 27 May 2021 20:29:00 +0200 Subject: [PATCH 09/11] Revert lru list elem clear fn --- Modules/_functoolsmodule.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 7a908b77362571..e2f35c5bd8a380 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -763,14 +763,6 @@ typedef struct lru_list_elem { PyObject *key, *result; } lru_list_elem; -static int -lru_list_elem_clear(lru_list_elem *link) -{ - Py_CLEAR(link->key); - Py_CLEAR(link->result); - return 0; -} - static int lru_list_elem_traverse(lru_list_elem *link, visitproc visit, void *arg) { @@ -794,7 +786,6 @@ lru_list_elem_dealloc(lru_list_elem *link) static PyType_Slot lru_list_elem_type_slots[] = { {Py_tp_dealloc, lru_list_elem_dealloc}, {Py_tp_traverse, lru_list_elem_traverse}, - {Py_tp_clear, lru_list_elem_clear}, {0, 0} }; From 63132470183f31b721b430fe3925ab22ace9f3fa Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Fri, 28 May 2021 09:47:35 +0200 Subject: [PATCH 10/11] Revert "Revert LRU cache element optimisation" This reverts commit fc39917b8808c70553ac4606154dfb628b559134. --- Modules/_functoolsmodule.c | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index e2f35c5bd8a380..10ffeb2122aad8 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -763,20 +763,10 @@ typedef struct lru_list_elem { PyObject *key, *result; } lru_list_elem; -static int -lru_list_elem_traverse(lru_list_elem *link, visitproc visit, void *arg) -{ - Py_VISIT(link->key); - Py_VISIT(link->result); - Py_VISIT(Py_TYPE(link)); - return 0; -} - static void lru_list_elem_dealloc(lru_list_elem *link) { PyTypeObject *tp = Py_TYPE(link); - PyObject_GC_UnTrack(link); Py_XDECREF(link->key); Py_XDECREF(link->result); tp->tp_free(link); @@ -785,15 +775,13 @@ lru_list_elem_dealloc(lru_list_elem *link) static PyType_Slot lru_list_elem_type_slots[] = { {Py_tp_dealloc, lru_list_elem_dealloc}, - {Py_tp_traverse, lru_list_elem_traverse}, {0, 0} }; static PyType_Spec lru_list_elem_type_spec = { .name = "functools._lru_list_elem", .basicsize = sizeof(lru_list_elem), - .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | - Py_TPFLAGS_HAVE_GC), + .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION, .slots = lru_list_elem_type_slots }; @@ -1058,8 +1046,8 @@ bounded_lru_cache_wrapper(lru_cache_object *self, PyObject *args, PyObject *kwds self->root.next == &self->root) { /* Cache is not full, so put the result in a new link */ - link = (lru_list_elem *)PyObject_GC_New(lru_list_elem, - self->lru_list_elem_type); + link = (lru_list_elem *)PyObject_New(lru_list_elem, + self->lru_list_elem_type); if (link == NULL) { Py_DECREF(key); Py_DECREF(result); @@ -1069,7 +1057,6 @@ bounded_lru_cache_wrapper(lru_cache_object *self, PyObject *args, PyObject *kwds link->hash = hash; link->key = key; link->result = result; - PyObject_GC_Track(link); /* What is really needed here is a SetItem variant with a "no clobber" option. If the __eq__ call triggers a reentrant call that adds this same key, then this setitem call will update the cache dict @@ -1359,7 +1346,9 @@ lru_cache_tp_traverse(lru_cache_object *self, visitproc visit, void *arg) lru_list_elem *link = self->root.next; while (link != &self->root) { lru_list_elem *next = link->next; - Py_VISIT(link); + Py_VISIT(link->key); + Py_VISIT(link->result); + Py_VISIT(Py_TYPE(link)); link = next; } Py_VISIT(self->cache); From 1539195e50d5f3c4d0da1f85b9eab84340e35021 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Fri, 28 May 2021 10:18:11 +0200 Subject: [PATCH 11/11] Restore lru_cache_tp_traverse --- Modules/_functoolsmodule.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 10ffeb2122aad8..fc3d925d94815b 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -1342,17 +1342,15 @@ lru_cache_deepcopy(PyObject *self, PyObject *unused) static int lru_cache_tp_traverse(lru_cache_object *self, visitproc visit, void *arg) { - Py_VISIT(Py_TYPE(self)); lru_list_elem *link = self->root.next; while (link != &self->root) { lru_list_elem *next = link->next; Py_VISIT(link->key); Py_VISIT(link->result); - Py_VISIT(Py_TYPE(link)); link = next; } - Py_VISIT(self->cache); Py_VISIT(self->func); + Py_VISIT(self->cache); Py_VISIT(self->kwd_mark); Py_VISIT(self->lru_list_elem_type); Py_VISIT(self->cache_info_type);