summaryrefslogtreecommitdiffstats
path: root/Objects/dictobject.c
diff options
context:
space:
mode:
authorAntoine Pitrou <solipsis@pitrou.net>2016-12-27 13:34:54 (GMT)
committerAntoine Pitrou <solipsis@pitrou.net>2016-12-27 13:34:54 (GMT)
commitc06ae208ebd55ff261438385c4179023f2df0388 (patch)
treefbf18b5e4e539f2fc4ca50c36023b43b6f62ee41 /Objects/dictobject.c
parenta171a03bd23c0fb9b424cce879d4c0b53fc331f6 (diff)
parentd741ed492f17609109432f1bccac0c019a05471b (diff)
downloadcpython-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.c90
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)
{