Skip to content

Commit 03f5abf

Browse files
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.
1 parent 6754566 commit 03f5abf

File tree

2 files changed

+11
-43
lines changed

2 files changed

+11
-43
lines changed

Lib/_weakrefset.py

Lines changed: 10 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -36,41 +36,26 @@ def __exit__(self, e, t, b):
3636
class WeakSet:
3737
def __init__(self, data=None):
3838
self.data = set()
39+
3940
def _remove(item, selfref=ref(self)):
4041
self = selfref()
4142
if self is not None:
42-
if self._iterating:
43-
self._pending_removals.append(item)
44-
else:
45-
self.data.discard(item)
43+
self.data.discard(item)
44+
4645
self._remove = _remove
47-
# A list of keys to be removed
48-
self._pending_removals = []
49-
self._iterating = set()
5046
if data is not None:
5147
self.update(data)
5248

53-
def _commit_removals(self):
54-
pop = self._pending_removals.pop
55-
discard = self.data.discard
56-
while True:
57-
try:
58-
item = pop()
59-
except IndexError:
60-
return
61-
discard(item)
62-
6349
def __iter__(self):
64-
with _IterationGuard(self):
65-
for itemref in self.data:
66-
item = itemref()
67-
if item is not None:
68-
# Caveat: the iterator will keep a strong reference to
69-
# `item` until it is resumed or closed.
70-
yield item
50+
for itemref in self.data.copy():
51+
item = itemref()
52+
if item is not None:
53+
# Caveat: the iterator will keep a strong reference to
54+
# `item` until it is resumed or closed.
55+
yield item
7156

7257
def __len__(self):
73-
return len(self.data) - len(self._pending_removals)
58+
return len(self.data)
7459

7560
def __contains__(self, item):
7661
try:
@@ -83,21 +68,15 @@ def __reduce__(self):
8368
return self.__class__, (list(self),), self.__getstate__()
8469

8570
def add(self, item):
86-
if self._pending_removals:
87-
self._commit_removals()
8871
self.data.add(ref(item, self._remove))
8972

9073
def clear(self):
91-
if self._pending_removals:
92-
self._commit_removals()
9374
self.data.clear()
9475

9576
def copy(self):
9677
return self.__class__(self)
9778

9879
def pop(self):
99-
if self._pending_removals:
100-
self._commit_removals()
10180
while True:
10281
try:
10382
itemref = self.data.pop()
@@ -108,18 +87,12 @@ def pop(self):
10887
return item
10988

11089
def remove(self, item):
111-
if self._pending_removals:
112-
self._commit_removals()
11390
self.data.remove(ref(item))
11491

11592
def discard(self, item):
116-
if self._pending_removals:
117-
self._commit_removals()
11893
self.data.discard(ref(item))
11994

12095
def update(self, other):
121-
if self._pending_removals:
122-
self._commit_removals()
12396
for element in other:
12497
self.add(element)
12598

@@ -136,8 +109,6 @@ def difference(self, other):
136109
def difference_update(self, other):
137110
self.__isub__(other)
138111
def __isub__(self, other):
139-
if self._pending_removals:
140-
self._commit_removals()
141112
if self is other:
142113
self.data.clear()
143114
else:
@@ -151,8 +122,6 @@ def intersection(self, other):
151122
def intersection_update(self, other):
152123
self.__iand__(other)
153124
def __iand__(self, other):
154-
if self._pending_removals:
155-
self._commit_removals()
156125
self.data.intersection_update(ref(item) for item in other)
157126
return self
158127

@@ -184,8 +153,6 @@ def symmetric_difference(self, other):
184153
def symmetric_difference_update(self, other):
185154
self.__ixor__(other)
186155
def __ixor__(self, other):
187-
if self._pending_removals:
188-
self._commit_removals()
189156
if self is other:
190157
self.data.clear()
191158
else:
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Make :class:`weakref.WeakSet` safe against concurrent mutations while it is being iterated. Patch by Kumar Aditya.

0 commit comments

Comments
 (0)