@@ -171,15 +171,24 @@ def _create_and_install_waiters(fs, return_when):
171
171
return waiter
172
172
173
173
174
- def _yield_and_decref (fs , ref_collect ):
174
+ def _yield_finished_futures (fs , waiter , ref_collect ):
175
175
"""
176
- Iterate on the list *fs*, yielding objects one by one in reverse order.
177
- Before yielding an object, it is removed from each set in
178
- the collection of sets *ref_collect*.
176
+ Iterate on the list *fs*, yielding finished futures one by one in
177
+ reverse order.
178
+ Before yielding a future, *waiter* is removed from its waiters
179
+ and the future is removed from each set in the collection of sets
180
+ *ref_collect*.
181
+
182
+ The aim of this function is to avoid keeping stale references after
183
+ the future is yielded and before the iterator resumes.
179
184
"""
180
185
while fs :
186
+ f = fs [- 1 ]
181
187
for futures_set in ref_collect :
182
- futures_set .remove (fs [- 1 ])
188
+ futures_set .remove (f )
189
+ with f ._condition :
190
+ f ._waiters .remove (waiter )
191
+ del f
183
192
# Careful not to keep a reference to the popped value
184
193
yield fs .pop ()
185
194
@@ -216,7 +225,8 @@ def as_completed(fs, timeout=None):
216
225
waiter = _create_and_install_waiters (fs , _AS_COMPLETED )
217
226
finished = list (finished )
218
227
try :
219
- yield from _yield_and_decref (finished , ref_collect = (fs ,))
228
+ yield from _yield_finished_futures (finished , waiter ,
229
+ ref_collect = (fs ,))
220
230
221
231
while pending :
222
232
if timeout is None :
@@ -237,9 +247,11 @@ def as_completed(fs, timeout=None):
237
247
238
248
# reverse to keep finishing order
239
249
finished .reverse ()
240
- yield from _yield_and_decref (finished , ref_collect = (fs , pending ))
250
+ yield from _yield_finished_futures (finished , waiter ,
251
+ ref_collect = (fs , pending ))
241
252
242
253
finally :
254
+ # Remove waiter from unfinished futures
243
255
for f in fs :
244
256
with f ._condition :
245
257
f ._waiters .remove (waiter )
0 commit comments