summaryrefslogtreecommitdiff
path: root/Objects/dictobject.c
diff options
context:
space:
mode:
authorAntoine Pitrou <solipsis@pitrou.net>2016-12-27 14:23:43 +0100
committerAntoine Pitrou <solipsis@pitrou.net>2016-12-27 14:23:43 +0100
commitd741ed492f17609109432f1bccac0c019a05471b (patch)
tree5b4425dfb7360f55993971d1b2fc9ebe7d1f00fa /Objects/dictobject.c
parent34d0ac8027e23609e24588735b37b8d5a55f7223 (diff)
parente10ca3a0fe10d825689179e9958c70aef01f4230 (diff)
downloadcpython-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.c91
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)
{