summaryrefslogtreecommitdiffstats
path: root/Modules
diff options
context:
space:
mode:
authorAntoine Pitrou <solipsis@pitrou.net>2016-12-27 13:23:43 (GMT)
committerAntoine Pitrou <solipsis@pitrou.net>2016-12-27 13:23:43 (GMT)
commitd741ed492f17609109432f1bccac0c019a05471b (patch)
tree5b4425dfb7360f55993971d1b2fc9ebe7d1f00fa /Modules
parent34d0ac8027e23609e24588735b37b8d5a55f7223 (diff)
parente10ca3a0fe10d825689179e9958c70aef01f4230 (diff)
downloadcpython-d741ed492f17609109432f1bccac0c019a05471b.zip
cpython-d741ed492f17609109432f1bccac0c019a05471b.tar.gz
cpython-d741ed492f17609109432f1bccac0c019a05471b.tar.bz2
Issue #28427: old keys should not remove new values from
WeakValueDictionary when collecting from another thread.
Diffstat (limited to 'Modules')
-rw-r--r--Modules/_weakref.c41
-rw-r--r--Modules/clinic/_weakref.c.h32
2 files changed, 72 insertions, 1 deletions
diff --git a/Modules/_weakref.c b/Modules/_weakref.c
index 805d6d0..f9c68d6 100644
--- a/Modules/_weakref.c
+++ b/Modules/_weakref.c
@@ -35,6 +35,46 @@ _weakref_getweakrefcount_impl(PyObject *module, PyObject *object)
}
+static int
+is_dead_weakref(PyObject *value)
+{
+ if (!PyWeakref_Check(value)) {
+ PyErr_SetString(PyExc_TypeError, "not a weakref");
+ return -1;
+ }
+ return PyWeakref_GET_OBJECT(value) == Py_None;
+}
+
+/*[clinic input]
+
+_weakref._remove_dead_weakref -> object
+
+ dct: object(subclass_of='&PyDict_Type')
+ key: object
+ /
+
+Atomically remove key from dict if it points to a dead weakref.
+[clinic start generated code]*/
+
+static PyObject *
+_weakref__remove_dead_weakref_impl(PyObject *module, PyObject *dct,
+ PyObject *key)
+/*[clinic end generated code: output=d9ff53061fcb875c input=19fc91f257f96a1d]*/
+{
+ if (_PyDict_DelItemIf(dct, key, is_dead_weakref) < 0) {
+ if (PyErr_ExceptionMatches(PyExc_KeyError))
+ /* This function is meant to allow safe weak-value dicts
+ with GC in another thread (see issue #28427), so it's
+ ok if the key doesn't exist anymore.
+ */
+ PyErr_Clear();
+ else
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+
PyDoc_STRVAR(weakref_getweakrefs__doc__,
"getweakrefs(object) -- return a list of all weak reference objects\n"
"that point to 'object'.");
@@ -88,6 +128,7 @@ weakref_proxy(PyObject *self, PyObject *args)
static PyMethodDef
weakref_functions[] = {
_WEAKREF_GETWEAKREFCOUNT_METHODDEF
+ _WEAKREF__REMOVE_DEAD_WEAKREF_METHODDEF
{"getweakrefs", weakref_getweakrefs, METH_O,
weakref_getweakrefs__doc__},
{"proxy", weakref_proxy, METH_VARARGS,
diff --git a/Modules/clinic/_weakref.c.h b/Modules/clinic/_weakref.c.h
index c192e72..ab84c30 100644
--- a/Modules/clinic/_weakref.c.h
+++ b/Modules/clinic/_weakref.c.h
@@ -29,4 +29,34 @@ _weakref_getweakrefcount(PyObject *module, PyObject *object)
exit:
return return_value;
}
-/*[clinic end generated code: output=e1ad587147323e19 input=a9049054013a1b77]*/
+
+PyDoc_STRVAR(_weakref__remove_dead_weakref__doc__,
+"_remove_dead_weakref($module, dct, key, /)\n"
+"--\n"
+"\n"
+"Atomically remove key from dict if it points to a dead weakref.");
+
+#define _WEAKREF__REMOVE_DEAD_WEAKREF_METHODDEF \
+ {"_remove_dead_weakref", (PyCFunction)_weakref__remove_dead_weakref, METH_VARARGS, _weakref__remove_dead_weakref__doc__},
+
+static PyObject *
+_weakref__remove_dead_weakref_impl(PyObject *module, PyObject *dct,
+ PyObject *key);
+
+static PyObject *
+_weakref__remove_dead_weakref(PyObject *module, PyObject *args)
+{
+ PyObject *return_value = NULL;
+ PyObject *dct;
+ PyObject *key;
+
+ if (!PyArg_ParseTuple(args, "O!O:_remove_dead_weakref",
+ &PyDict_Type, &dct, &key)) {
+ goto exit;
+ }
+ return_value = _weakref__remove_dead_weakref_impl(module, dct, key);
+
+exit:
+ return return_value;
+}
+/*[clinic end generated code: output=e860dd818a44bc9b input=a9049054013a1b77]*/