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:25:09 2016 +0300 @@ -619,74 +619,14 @@ sequence is empty."); /* this object is used delimit args and keywords in the cache keys */ static PyObject *kwd_mark = NULL; +static PyObject *sentinel = 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,116 +761,63 @@ 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; -} +PyObject * +_PyDict_Pop_KnownHash(PyDictObject *mp, PyObject *key, PyObject *deflt, Py_hash_t hash); 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_Pop_KnownHash((PyDictObject *)self->cache, key, sentinel, hash); + if (result == NULL) { + Py_DECREF(key); + return NULL; + } + if (result != sentinel) { + if (_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; } - if (PyErr_Occurred()) { - Py_DECREF(key); - return NULL; - } + Py_DECREF(result); result = PyObject_Call(self->func, args, kwds); if (!result) { 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)) { + Py_INCREF(firstkey); + if (_PyDict_DelItem_KnownHash(self->cache, firstkey, firsthash) < 0) { + Py_DECREF(firstkey); + Py_DECREF(result); + Py_DECREF(key); + return NULL; + } + Py_DECREF(firstkey); } - /* 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 +873,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 +887,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 +925,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 +954,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 +965,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; } @@ -1212,6 +1063,7 @@ static void module_free(void *m) { Py_CLEAR(kwd_mark); + Py_CLEAR(sentinel); } static struct PyModuleDef _functoolsmodule = { @@ -1248,6 +1100,12 @@ PyInit__functools(void) return NULL; } + sentinel = PyObject_CallObject((PyObject *)&PyBaseObject_Type, NULL); + if (!sentinel) { + Py_DECREF(m); + return NULL; + } + for (i=0 ; typelist[i] != NULL ; i++) { if (PyType_Ready(typelist[i]) < 0) { Py_DECREF(m); diff -r 128da01095a2 Objects/dictobject.c --- a/Objects/dictobject.c Wed Sep 21 09:16:39 2016 +0200 +++ b/Objects/dictobject.c Wed Sep 21 12:25:09 2016 +0300 @@ -1833,6 +1833,59 @@ PyObject * return old_value; } +PyObject * +_PyDict_Pop_KnownHash(PyDictObject *mp, PyObject *key, PyObject *deflt, Py_hash_t hash) +{ + Py_ssize_t ix, hashpos; + PyObject *old_value, *old_key; + PyDictKeyEntry *ep; + PyObject **value_addr; + + if (mp->ma_used == 0) { + if (deflt) { + Py_INCREF(deflt); + return deflt; + } + _PyErr_SetKeyError(key); + return NULL; + } + ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr, &hashpos); + if (ix == DKIX_ERROR) + return NULL; + if (ix == DKIX_EMPTY || *value_addr == NULL) { + if (deflt) { + Py_INCREF(deflt); + return deflt; + } + _PyErr_SetKeyError(key); + return NULL; + } + + // Split table doesn't allow deletion. Combine it. + if (_PyDict_HasSplitTable(mp)) { + if (dictresize(mp, DK_SIZE(mp->ma_keys))) { + return NULL; + } + ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr, &hashpos); + assert(ix >= 0); + } + + old_value = *value_addr; + assert(old_value != NULL); + *value_addr = NULL; + mp->ma_used--; + mp->ma_version_tag = DICT_NEXT_VERSION(); + dk_set_index(mp->ma_keys, hashpos, DKIX_DUMMY); + ep = &DK_ENTRIES(mp->ma_keys)[ix]; + ENSURE_ALLOWS_DELETIONS(mp); + old_key = ep->me_key; + ep->me_key = NULL; + Py_DECREF(old_key); + + assert(_PyDict_CheckConsistency(mp)); + return old_value; +} + /* Internal version of dict.from_keys(). It is subclass-friendly. */ PyObject * _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value)