Skip to content

Commit

Permalink
bpo-20369: concurrent.futures.wait() now deduplicates futures given a… (
Browse files Browse the repository at this point in the history
pythonGH-30168)

* bpo-20369: concurrent.futures.wait() now deduplicates futures given as arg.

* 📜🤖 Added by blurb_it.

Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
  • Loading branch information
kumaraditya303 and blurb-it[bot] committed Jan 4, 2022
1 parent b949845 commit 7d7817c
Show file tree
Hide file tree
Showing 4 changed files with 18 additions and 7 deletions.
3 changes: 2 additions & 1 deletion Doc/library/concurrent.futures.rst
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,8 @@ Module Functions
.. function:: wait(fs, timeout=None, return_when=ALL_COMPLETED)

Wait for the :class:`Future` instances (possibly created by different
:class:`Executor` instances) given by *fs* to complete. Returns a named
:class:`Executor` instances) given by *fs* to complete. Duplicate futures
given to *fs* are removed and will be returned only once. Returns a named
2-tuple of sets. The first set, named ``done``, contains the futures that
completed (finished or cancelled futures) before the wait completed. The
second set, named ``not_done``, contains the futures that did not complete
Expand Down
13 changes: 7 additions & 6 deletions Lib/concurrent/futures/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,13 +282,14 @@ def wait(fs, timeout=None, return_when=ALL_COMPLETED):
A named 2-tuple of sets. The first set, named 'done', contains the
futures that completed (is finished or cancelled) before the wait
completed. The second set, named 'not_done', contains uncompleted
futures.
futures. Duplicate futures given to *fs* are removed and will be
returned only once.
"""
fs = set(fs)
with _AcquireFutures(fs):
done = set(f for f in fs
if f._state in [CANCELLED_AND_NOTIFIED, FINISHED])
not_done = set(fs) - done

done = {f for f in fs
if f._state in [CANCELLED_AND_NOTIFIED, FINISHED]}
not_done = fs - done
if (return_when == FIRST_COMPLETED) and done:
return DoneAndNotDoneFutures(done, not_done)
elif (return_when == FIRST_EXCEPTION) and done:
Expand All @@ -307,7 +308,7 @@ def wait(fs, timeout=None, return_when=ALL_COMPLETED):
f._waiters.remove(waiter)

done.update(waiter.finished_futures)
return DoneAndNotDoneFutures(done, set(fs) - done)
return DoneAndNotDoneFutures(done, fs - done)

class Future(object):
"""Represents the result of an asynchronous computation."""
Expand Down
8 changes: 8 additions & 0 deletions Lib/test/test_concurrent_futures.py
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,14 @@ def test_shutdown_no_wait(self):


class WaitTests:
def test_20369(self):
# See https://bugs.python.org/issue20369
future = self.executor.submit(time.sleep, 1.5)
done, not_done = futures.wait([future, future],
return_when=futures.ALL_COMPLETED)
self.assertEqual({future}, done)
self.assertEqual(set(), not_done)


def test_first_completed(self):
future1 = self.executor.submit(mul, 21, 2)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
:func:`concurrent.futures.wait` no longer blocks forever when given duplicate Futures. Patch by Kumar Aditya.

0 comments on commit 7d7817c

Please sign in to comment.