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 /Lib/weakref.py | |
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 'Lib/weakref.py')
-rw-r--r-- | Lib/weakref.py | 34 |
1 files changed, 30 insertions, 4 deletions
diff --git a/Lib/weakref.py b/Lib/weakref.py index 9f8ef3e..aaebd0c 100644 --- a/Lib/weakref.py +++ b/Lib/weakref.py @@ -16,7 +16,8 @@ from _weakref import ( proxy, CallableProxyType, ProxyType, - ReferenceType) + ReferenceType, + _remove_dead_weakref) from _weakrefset import WeakSet, _IterationGuard @@ -111,7 +112,9 @@ class WeakValueDictionary(collections.MutableMapping): if self._iterating: self._pending_removals.append(wr.key) else: - del self.data[wr.key] + # Atomic removal is necessary since this function + # can be called asynchronously by the GC + _remove_dead_weakref(d, wr.key) self._remove = remove # A list of keys to be removed self._pending_removals = [] @@ -125,9 +128,12 @@ class WeakValueDictionary(collections.MutableMapping): # We shouldn't encounter any KeyError, because this method should # always be called *before* mutating the dict. while l: - del d[l.pop()] + key = l.pop() + _remove_dead_weakref(d, key) def __getitem__(self, key): + if self._pending_removals: + self._commit_removals() o = self.data[key]() if o is None: raise KeyError(key) @@ -140,9 +146,13 @@ class WeakValueDictionary(collections.MutableMapping): del self.data[key] def __len__(self): - return len(self.data) - len(self._pending_removals) + if self._pending_removals: + self._commit_removals() + return len(self.data) def __contains__(self, key): + if self._pending_removals: + self._commit_removals() try: o = self.data[key]() except KeyError: @@ -158,6 +168,8 @@ class WeakValueDictionary(collections.MutableMapping): self.data[key] = KeyedRef(value, self._remove, key) def copy(self): + if self._pending_removals: + self._commit_removals() new = WeakValueDictionary() for key, wr in self.data.items(): o = wr() @@ -169,6 +181,8 @@ class WeakValueDictionary(collections.MutableMapping): def __deepcopy__(self, memo): from copy import deepcopy + if self._pending_removals: + self._commit_removals() new = self.__class__() for key, wr in self.data.items(): o = wr() @@ -177,6 +191,8 @@ class WeakValueDictionary(collections.MutableMapping): return new def get(self, key, default=None): + if self._pending_removals: + self._commit_removals() try: wr = self.data[key] except KeyError: @@ -190,6 +206,8 @@ class WeakValueDictionary(collections.MutableMapping): return o def items(self): + if self._pending_removals: + self._commit_removals() with _IterationGuard(self): for k, wr in self.data.items(): v = wr() @@ -197,6 +215,8 @@ class WeakValueDictionary(collections.MutableMapping): yield k, v def keys(self): + if self._pending_removals: + self._commit_removals() with _IterationGuard(self): for k, wr in self.data.items(): if wr() is not None: @@ -214,10 +234,14 @@ class WeakValueDictionary(collections.MutableMapping): keep the values around longer than needed. """ + if self._pending_removals: + self._commit_removals() with _IterationGuard(self): yield from self.data.values() def values(self): + if self._pending_removals: + self._commit_removals() with _IterationGuard(self): for wr in self.data.values(): obj = wr() @@ -290,6 +314,8 @@ class WeakValueDictionary(collections.MutableMapping): keep the values around longer than needed. """ + if self._pending_removals: + self._commit_removals() return list(self.data.values()) |