diff options
author | Antoine Pitrou <solipsis@pitrou.net> | 2016-12-27 14:23:43 +0100 |
---|---|---|
committer | Antoine Pitrou <solipsis@pitrou.net> | 2016-12-27 14:23:43 +0100 |
commit | d741ed492f17609109432f1bccac0c019a05471b (patch) | |
tree | 5b4425dfb7360f55993971d1b2fc9ebe7d1f00fa /Objects/dictobject.c | |
parent | 34d0ac8027e23609e24588735b37b8d5a55f7223 (diff) | |
parent | e10ca3a0fe10d825689179e9958c70aef01f4230 (diff) | |
download | cpython-git-d741ed492f17609109432f1bccac0c019a05471b.tar.gz |
Issue #28427: old keys should not remove new values from
WeakValueDictionary when collecting from another thread.
Diffstat (limited to 'Objects/dictobject.c')
-rw-r--r-- | Objects/dictobject.c | 91 |
1 files changed, 74 insertions, 17 deletions
diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 5fff34b119..64941937e7 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -1591,11 +1591,34 @@ _PyDict_SetItem_KnownHash(PyObject *op, PyObject *key, PyObject *value, return insertdict(mp, key, hash, value); } +static int +delitem_common(PyDictObject *mp, Py_ssize_t hashpos, Py_ssize_t ix, + PyObject **value_addr) +{ + PyObject *old_key, *old_value; + PyDictKeyEntry *ep; + + old_value = *value_addr; + assert(old_value != NULL); + *value_addr = NULL; + mp->ma_used--; + mp->ma_version_tag = DICT_NEXT_VERSION(); + ep = &DK_ENTRIES(mp->ma_keys)[ix]; + dk_set_index(mp->ma_keys, hashpos, DKIX_DUMMY); + ENSURE_ALLOWS_DELETIONS(mp); + old_key = ep->me_key; + ep->me_key = NULL; + Py_DECREF(old_key); + Py_DECREF(old_value); + + assert(_PyDict_CheckConsistency(mp)); + return 0; +} + int PyDict_DelItem(PyObject *op, PyObject *key) { Py_hash_t hash; - assert(key); if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { @@ -1612,8 +1635,6 @@ _PyDict_DelItem_KnownHash(PyObject *op, PyObject *key, Py_hash_t hash) { Py_ssize_t hashpos, ix; PyDictObject *mp; - PyDictKeyEntry *ep; - PyObject *old_key, *old_value; PyObject **value_addr; if (!PyDict_Check(op)) { @@ -1640,24 +1661,60 @@ _PyDict_DelItem_KnownHash(PyObject *op, PyObject *key, Py_hash_t hash) ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr, &hashpos); assert(ix >= 0); } + return delitem_common(mp, hashpos, ix, value_addr); +} - old_value = *value_addr; - assert(old_value != NULL); - *value_addr = NULL; - mp->ma_used--; - mp->ma_version_tag = DICT_NEXT_VERSION(); - ep = &DK_ENTRIES(mp->ma_keys)[ix]; - dk_set_index(mp->ma_keys, hashpos, DKIX_DUMMY); - ENSURE_ALLOWS_DELETIONS(mp); - old_key = ep->me_key; - ep->me_key = NULL; - Py_DECREF(old_key); - Py_DECREF(old_value); +/* This function promises that the predicate -> deletion sequence is atomic + * (i.e. protected by the GIL), assuming the predicate itself doesn't + * release the GIL. + */ +int +_PyDict_DelItemIf(PyObject *op, PyObject *key, + int (*predicate)(PyObject *value)) +{ + Py_ssize_t hashpos, ix; + PyDictObject *mp; + Py_hash_t hash; + PyObject **value_addr; + int res; - assert(_PyDict_CheckConsistency(mp)); - return 0; + if (!PyDict_Check(op)) { + PyErr_BadInternalCall(); + return -1; + } + assert(key); + hash = PyObject_Hash(key); + if (hash == -1) + return -1; + mp = (PyDictObject *)op; + ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr, &hashpos); + if (ix == DKIX_ERROR) + return -1; + if (ix == DKIX_EMPTY || *value_addr == NULL) { + _PyErr_SetKeyError(key); + return -1; + } + assert(dk_get_index(mp->ma_keys, hashpos) == ix); + + // Split table doesn't allow deletion. Combine it. + if (_PyDict_HasSplitTable(mp)) { + if (dictresize(mp, DK_SIZE(mp->ma_keys))) { + return -1; + } + ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr, &hashpos); + assert(ix >= 0); + } + + res = predicate(*value_addr); + if (res == -1) + return -1; + if (res > 0) + return delitem_common(mp, hashpos, ix, value_addr); + else + return 0; } + void PyDict_Clear(PyObject *op) { |