diff options
author | Antoine Pitrou <solipsis@pitrou.net> | 2016-12-27 13:34:54 (GMT) |
---|---|---|
committer | Antoine Pitrou <solipsis@pitrou.net> | 2016-12-27 13:34:54 (GMT) |
commit | c06ae208ebd55ff261438385c4179023f2df0388 (patch) | |
tree | fbf18b5e4e539f2fc4ca50c36023b43b6f62ee41 /Objects/dictobject.c | |
parent | a171a03bd23c0fb9b424cce879d4c0b53fc331f6 (diff) | |
parent | d741ed492f17609109432f1bccac0c019a05471b (diff) | |
download | cpython-c06ae208ebd55ff261438385c4179023f2df0388.zip cpython-c06ae208ebd55ff261438385c4179023f2df0388.tar.gz cpython-c06ae208ebd55ff261438385c4179023f2df0388.tar.bz2 |
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 | 90 |
1 files changed, 74 insertions, 16 deletions
diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 6a7e831..baef589 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -1583,11 +1583,32 @@ _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 *old_value) +{ + PyObject *old_key; + PyDictKeyEntry *ep; + + 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; + ep->me_value = 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) { @@ -1604,8 +1625,7 @@ _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 *old_value; if (!PyDict_Check(op)) { PyErr_BadInternalCall(); @@ -1632,22 +1652,60 @@ _PyDict_DelItem_KnownHash(PyObject *op, PyObject *key, Py_hash_t hash) assert(ix >= 0); } - assert(old_value != 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; - ep->me_value = NULL; - Py_DECREF(old_key); - Py_DECREF(old_value); + return delitem_common(mp, hashpos, ix, old_value); +} - assert(_PyDict_CheckConsistency(mp)); - return 0; +/* 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 *old_value; + int res; + + 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, &old_value, &hashpos); + if (ix == DKIX_ERROR) + return -1; + if (ix == DKIX_EMPTY || old_value == 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, &old_value, &hashpos); + assert(ix >= 0); + } + + res = predicate(old_value); + if (res == -1) + return -1; + if (res > 0) + return delitem_common(mp, hashpos, ix, old_value); + else + return 0; } + void PyDict_Clear(PyObject *op) { |