https://github.com/python/cpython/commit/03f5abf15a20f6e623282a393bc2a0affac69bb0
commit: 03f5abf15a20f6e623282a393bc2a0affac69bb0
branch: main
author: Kumar Aditya <[email protected]>
committer: kumaraditya303 <[email protected]>
date: 2024-08-27T13:04:03Z
summary:
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.
files:
A Misc/NEWS.d/next/Library/2024-08-27-12-38-42.gh-issue-123089.vA7iFR.rst
M Lib/_weakrefset.py
diff --git a/Lib/_weakrefset.py b/Lib/_weakrefset.py
index 489eec714e0d48..2071755d71dfc8 100644
--- a/Lib/_weakrefset.py
+++ b/Lib/_weakrefset.py
@@ -36,41 +36,26 @@ def __exit__(self, e, t, b):
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 @@ def __reduce__(self):
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 @@ def pop(self):
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 @@ def difference(self, other):
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 @@ def intersection(self, other):
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 @@ def symmetric_difference(self, other):
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 00000000000000..74cbdd551350f7
--- /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.
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]