summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKumar Aditya <kumaraditya@python.org>2024-08-27 13:04:03 (GMT)
committerGitHub <noreply@github.com>2024-08-27 13:04:03 (GMT)
commit03f5abf15a20f6e623282a393bc2a0affac69bb0 (patch)
tree1512e4add5d1f42b56c3890a9cf88119215c8483
parent6754566a51a5706e8c9da0094b892113311ba20c (diff)
downloadcpython-03f5abf15a20f6e623282a393bc2a0affac69bb0.zip
cpython-03f5abf15a20f6e623282a393bc2a0affac69bb0.tar.gz
cpython-03f5abf15a20f6e623282a393bc2a0affac69bb0.tar.bz2
gh-123089: Make weakref.WeakSet safe against concurrent mutations while it is being iterated (#123279)
* Make `weakref.WeakSet` safe against concurrent mutations while it is being iterated. `_IterationGuard` is no longer used for `WeakSet`, it now relies on copying the underlying set which is an atomic operation while iterating so that it can be modified by other threads.
-rw-r--r--Lib/_weakrefset.py53
-rw-r--r--Misc/NEWS.d/next/Library/2024-08-27-12-38-42.gh-issue-123089.vA7iFR.rst1
2 files changed, 11 insertions, 43 deletions
diff --git a/Lib/_weakrefset.py b/Lib/_weakrefset.py
index 489eec7..2071755 100644
--- a/Lib/_weakrefset.py
+++ b/Lib/_weakrefset.py
@@ -36,41 +36,26 @@ class _IterationGuard:
class WeakSet:
def __init__(self, data=None):
self.data = set()
+
def _remove(item, selfref=ref(self)):
self = selfref()
if self is not None:
- if self._iterating:
- self._pending_removals.append(item)
- else:
- self.data.discard(item)
+ self.data.discard(item)
+
self._remove = _remove
- # A list of keys to be removed
- self._pending_removals = []
- self._iterating = set()
if data is not None:
self.update(data)
- def _commit_removals(self):
- pop = self._pending_removals.pop
- discard = self.data.discard
- while True:
- try:
- item = pop()
- except IndexError:
- return
- discard(item)
-
def __iter__(self):
- with _IterationGuard(self):
- for itemref in self.data:
- item = itemref()
- if item is not None:
- # Caveat: the iterator will keep a strong reference to
- # `item` until it is resumed or closed.
- yield item
+ for itemref in self.data.copy():
+ item = itemref()
+ if item is not None:
+ # Caveat: the iterator will keep a strong reference to
+ # `item` until it is resumed or closed.
+ yield item
def __len__(self):
- return len(self.data) - len(self._pending_removals)
+ return len(self.data)
def __contains__(self, item):
try:
@@ -83,21 +68,15 @@ class WeakSet:
return self.__class__, (list(self),), self.__getstate__()
def add(self, item):
- if self._pending_removals:
- self._commit_removals()
self.data.add(ref(item, self._remove))
def clear(self):
- if self._pending_removals:
- self._commit_removals()
self.data.clear()
def copy(self):
return self.__class__(self)
def pop(self):
- if self._pending_removals:
- self._commit_removals()
while True:
try:
itemref = self.data.pop()
@@ -108,18 +87,12 @@ class WeakSet:
return item
def remove(self, item):
- if self._pending_removals:
- self._commit_removals()
self.data.remove(ref(item))
def discard(self, item):
- if self._pending_removals:
- self._commit_removals()
self.data.discard(ref(item))
def update(self, other):
- if self._pending_removals:
- self._commit_removals()
for element in other:
self.add(element)
@@ -136,8 +109,6 @@ class WeakSet:
def difference_update(self, other):
self.__isub__(other)
def __isub__(self, other):
- if self._pending_removals:
- self._commit_removals()
if self is other:
self.data.clear()
else:
@@ -151,8 +122,6 @@ class WeakSet:
def intersection_update(self, other):
self.__iand__(other)
def __iand__(self, other):
- if self._pending_removals:
- self._commit_removals()
self.data.intersection_update(ref(item) for item in other)
return self
@@ -184,8 +153,6 @@ class WeakSet:
def symmetric_difference_update(self, other):
self.__ixor__(other)
def __ixor__(self, other):
- if self._pending_removals:
- self._commit_removals()
if self is other:
self.data.clear()
else:
diff --git a/Misc/NEWS.d/next/Library/2024-08-27-12-38-42.gh-issue-123089.vA7iFR.rst b/Misc/NEWS.d/next/Library/2024-08-27-12-38-42.gh-issue-123089.vA7iFR.rst
new file mode 100644
index 0000000..74cbdd5
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-08-27-12-38-42.gh-issue-123089.vA7iFR.rst
@@ -0,0 +1 @@
+Make :class:`weakref.WeakSet` safe against concurrent mutations while it is being iterated. Patch by Kumar Aditya.