diff -r fae92299eb68 Lib/concurrent/futures/_base.py --- a/Lib/concurrent/futures/_base.py Sat Jan 25 13:27:53 2014 -0500 +++ b/Lib/concurrent/futures/_base.py Sat Jan 25 16:36:48 2014 -0500 @@ -249,12 +249,14 @@ 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. If any given Futures are duplicated, they will be returned + 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 + not_done = fs - done if (return_when == FIRST_COMPLETED) and done: return DoneAndNotDoneFutures(done, not_done) diff -r fae92299eb68 Lib/test/test_concurrent_futures.py --- a/Lib/test/test_concurrent_futures.py Sat Jan 25 13:27:53 2014 -0500 +++ b/Lib/test/test_concurrent_futures.py Sat Jan 25 16:36:48 2014 -0500 @@ -290,6 +290,21 @@ future1]), finished) self.assertEqual(set([future2]), pending) + def test_duplicate_futures(self): + # Issue #20369: wait() on duplicate finished futures should not trigger + # waiters to be installed. + future1 = self.executor.submit(mul, 6, 7) + time.sleep(1) + self.assertEqual(future1._state, FINISHED, "Precondition not valid for test") + start = time.time() + done, notdone = futures.wait([future1,future1], + return_when=futures.ALL_COMPLETED, + timeout=2) + end = time.time() + self.assertEqual( set([future1]), done) + self.assertEqual( len(notdone), 0) + self.assertLess(end-start, 2, "Wait timed out on finished duplicated futures") + class ThreadPoolWaitTests(ThreadPoolMixin, WaitTests, unittest.TestCase):