diff -r 128da01095a2 Modules/_functoolsmodule.c --- a/Modules/_functoolsmodule.c Wed Sep 21 09:16:39 2016 +0200 +++ b/Modules/_functoolsmodule.c Wed Sep 21 12:29:33 2016 +0300 @@ -620,73 +620,12 @@ sequence is empty."); /* this object is used delimit args and keywords in the cache keys */ static PyObject *kwd_mark = NULL; -struct lru_list_elem; struct lru_cache_object; -typedef struct lru_list_elem { - PyObject_HEAD - struct lru_list_elem *prev, *next; /* borrowed links */ - Py_hash_t hash; - PyObject *key, *result; -} lru_list_elem; - -static void -lru_list_elem_dealloc(lru_list_elem *link) -{ - _PyObject_GC_UNTRACK(link); - Py_XDECREF(link->key); - Py_XDECREF(link->result); - PyObject_GC_Del(link); -} - -static int -lru_list_elem_traverse(lru_list_elem *link, visitproc visit, void *arg) -{ - Py_VISIT(link->key); - Py_VISIT(link->result); - return 0; -} - -static int -lru_list_elem_clear(lru_list_elem *link) -{ - Py_CLEAR(link->key); - Py_CLEAR(link->result); - return 0; -} - -static PyTypeObject lru_list_elem_type = { - PyVarObject_HEAD_INIT(&PyType_Type, 0) - "functools._lru_list_elem", /* tp_name */ - sizeof(lru_list_elem), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)lru_list_elem_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_reserved */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ - 0, /* tp_doc */ - (traverseproc)lru_list_elem_traverse, /* tp_traverse */ - (inquiry)lru_list_elem_clear, /* tp_clear */ -}; - - typedef PyObject *(*lru_cache_ternaryfunc)(struct lru_cache_object *, PyObject *, PyObject *); typedef struct lru_cache_object { - lru_list_elem root; /* includes PyObject_HEAD */ + PyObject_HEAD Py_ssize_t maxsize; PyObject *maxsize_O; PyObject *func; @@ -821,43 +760,27 @@ infinite_lru_cache_wrapper(lru_cache_obj return result; } -static void -lru_cache_extricate_link(lru_list_elem *link) -{ - link->prev->next = link->next; - link->next->prev = link->prev; -} - -static void -lru_cache_append_link(lru_cache_object *self, lru_list_elem *link) -{ - lru_list_elem *root = &self->root; - lru_list_elem *last = root->prev; - last->next = root->prev = link; - link->prev = last; - link->next = root; -} - static PyObject * bounded_lru_cache_wrapper(lru_cache_object *self, PyObject *args, PyObject *kwds) { - lru_list_elem *link; - PyObject *key, *result; + PyObject *result; Py_hash_t hash; - - key = lru_cache_make_key(args, kwds, self->typed); + PyObject *key = lru_cache_make_key(args, kwds, self->typed); if (!key) return NULL; hash = PyObject_Hash(key); if (hash == -1) return NULL; - link = (lru_list_elem *)_PyDict_GetItem_KnownHash(self->cache, key, hash); - if (link) { - lru_cache_extricate_link(link); - lru_cache_append_link(self, link); + result = _PyDict_GetItem_KnownHash(self->cache, key, hash); + if (result) { + Py_INCREF(result); + if (_PyDict_DelItem_KnownHash(self->cache, key, hash) < 0 || + _PyDict_SetItem_KnownHash(self->cache, key, result, hash) < 0) { + Py_DECREF(result); + Py_DECREF(key); + return NULL; + } self->hits++; - result = link->result; - Py_INCREF(result); Py_DECREF(key); return result; } @@ -870,67 +793,25 @@ bounded_lru_cache_wrapper(lru_cache_obje Py_DECREF(key); return NULL; } - if (self->full && self->root.next != &self->root) { - /* Use the oldest item to store the new key and result. */ - PyObject *oldkey, *oldresult; - /* Extricate the oldest item. */ - link = self->root.next; - lru_cache_extricate_link(link); - /* Remove it from the cache. - The cache dict holds one reference to the link, - and the linked list holds yet one reference to it. */ - if (_PyDict_DelItem_KnownHash(self->cache, link->key, - link->hash) < 0) { - lru_cache_append_link(self, link); - Py_DECREF(key); - Py_DECREF(result); - return NULL; + if (self->full) { + Py_ssize_t pos = 0; + PyObject *firstkey; + Py_hash_t firsthash; + if (_PyDict_Next(self->cache, &pos, &firstkey, NULL, &firsthash)) { + if (_PyDict_DelItem_KnownHash(self->cache, firstkey, firsthash) < 0) { + Py_DECREF(result); + Py_DECREF(key); + return NULL; + } } - /* Keep a reference to the old key and old result to - prevent their ref counts from going to zero during the - update. That will prevent potentially arbitrary object - clean-up code (i.e. __del__) from running while we're - still adjusting the links. */ - oldkey = link->key; - oldresult = link->result; - - link->hash = hash; - link->key = key; - link->result = result; - if (_PyDict_SetItem_KnownHash(self->cache, key, (PyObject *)link, - hash) < 0) { - Py_DECREF(link); - Py_DECREF(oldkey); - Py_DECREF(oldresult); - return NULL; - } - lru_cache_append_link(self, link); - Py_INCREF(result); /* for return */ - Py_DECREF(oldkey); - Py_DECREF(oldresult); - } else { - /* Put result in a new link at the front of the queue. */ - link = (lru_list_elem *)PyObject_GC_New(lru_list_elem, - &lru_list_elem_type); - if (link == NULL) { - Py_DECREF(key); - Py_DECREF(result); - return NULL; - } - - link->hash = hash; - link->key = key; - link->result = result; - _PyObject_GC_TRACK(link); - if (_PyDict_SetItem_KnownHash(self->cache, key, (PyObject *)link, - hash) < 0) { - Py_DECREF(link); - return NULL; - } - lru_cache_append_link(self, link); - Py_INCREF(result); /* for return */ - self->full = (PyDict_Size(self->cache) >= self->maxsize); } + if (_PyDict_SetItem_KnownHash(self->cache, key, result, hash) < 0) { + Py_DECREF(result); + Py_DECREF(key); + return NULL; + } + self->full = (PyDict_Size(self->cache) >= self->maxsize); + Py_DECREF(key); self->misses++; return result; } @@ -986,8 +867,6 @@ lru_cache_new(PyTypeObject *type, PyObje } obj->cache = cachedict; - obj->root.prev = &obj->root; - obj->root.next = &obj->root; obj->maxsize = maxsize; Py_INCREF(maxsize_O); obj->maxsize_O = maxsize_O; @@ -1002,38 +881,14 @@ lru_cache_new(PyTypeObject *type, PyObje return (PyObject *)obj; } -static lru_list_elem * -lru_cache_unlink_list(lru_cache_object *self) -{ - lru_list_elem *root = &self->root; - lru_list_elem *link = root->next; - if (link == root) - return NULL; - root->prev->next = NULL; - root->next = root->prev = root; - return link; -} - -static void -lru_cache_clear_list(lru_list_elem *link) -{ - while (link != NULL) { - lru_list_elem *next = link->next; - Py_DECREF(link); - link = next; - } -} - static void lru_cache_dealloc(lru_cache_object *obj) { - lru_list_elem *list = lru_cache_unlink_list(obj); Py_XDECREF(obj->maxsize_O); Py_XDECREF(obj->func); Py_XDECREF(obj->cache); Py_XDECREF(obj->dict); Py_XDECREF(obj->cache_info_type); - lru_cache_clear_list(list); Py_TYPE(obj)->tp_free(obj); } @@ -1064,11 +919,9 @@ lru_cache_cache_info(lru_cache_object *s static PyObject * lru_cache_cache_clear(lru_cache_object *self, PyObject *unused) { - lru_list_elem *list = lru_cache_unlink_list(self); self->hits = self->misses = 0; self->full = 0; PyDict_Clear(self->cache); - lru_cache_clear_list(list); Py_RETURN_NONE; } @@ -1095,12 +948,6 @@ lru_cache_deepcopy(PyObject *self, PyObj static int 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); - link = next; - } Py_VISIT(self->maxsize_O); Py_VISIT(self->func); Py_VISIT(self->cache); @@ -1112,13 +959,11 @@ lru_cache_tp_traverse(lru_cache_object * static int lru_cache_tp_clear(lru_cache_object *self) { - lru_list_elem *list = lru_cache_unlink_list(self); Py_CLEAR(self->maxsize_O); Py_CLEAR(self->func); Py_CLEAR(self->cache); Py_CLEAR(self->cache_info_type); Py_CLEAR(self->dict); - lru_cache_clear_list(list); return 0; }