| 
									
										
										
										
											2023-08-24 19:21:44 +02:00
										 |  |  | import sys | 
					
						
							|  |  |  | import threading | 
					
						
							|  |  |  | import unittest | 
					
						
							|  |  |  | from concurrent import futures | 
					
						
							| 
									
										
										
										
											2023-09-05 17:56:30 +03:00
										 |  |  | from test import support | 
					
						
							| 
									
										
										
										
											2025-03-06 12:30:58 -05:00
										 |  |  | from test.support import threading_helper | 
					
						
							| 
									
										
										
										
											2023-08-24 19:21:44 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | from .util import ( | 
					
						
							|  |  |  |     CANCELLED_FUTURE, CANCELLED_AND_NOTIFIED_FUTURE, EXCEPTION_FUTURE, | 
					
						
							|  |  |  |     SUCCESSFUL_FUTURE, | 
					
						
							|  |  |  |     create_executor_tests, setup_module, | 
					
						
							|  |  |  |     BaseTestCase, ThreadPoolMixin, | 
					
						
							|  |  |  |     ProcessPoolForkMixin, ProcessPoolForkserverMixin, ProcessPoolSpawnMixin) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def mul(x, y): | 
					
						
							|  |  |  |     return x * y | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-06 12:30:58 -05:00
										 |  |  | def wait_and_raise(e): | 
					
						
							|  |  |  |     e.wait() | 
					
						
							| 
									
										
										
										
											2023-08-24 19:21:44 +02:00
										 |  |  |     raise Exception('this is an exception') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class WaitTests: | 
					
						
							|  |  |  |     def test_20369(self): | 
					
						
							|  |  |  |         # See https://bugs.python.org/issue20369 | 
					
						
							| 
									
										
										
										
											2025-03-06 12:30:58 -05:00
										 |  |  |         future = self.executor.submit(mul, 1, 2) | 
					
						
							| 
									
										
										
										
											2023-08-24 19:21:44 +02:00
										 |  |  |         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): | 
					
						
							| 
									
										
										
										
											2025-03-06 12:30:58 -05:00
										 |  |  |         event = self.create_event() | 
					
						
							| 
									
										
										
										
											2023-08-24 19:21:44 +02:00
										 |  |  |         future1 = self.executor.submit(mul, 21, 2) | 
					
						
							| 
									
										
										
										
											2025-03-06 12:30:58 -05:00
										 |  |  |         future2 = self.executor.submit(event.wait) | 
					
						
							| 
									
										
										
										
											2023-08-24 19:21:44 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-06 12:30:58 -05:00
										 |  |  |         try: | 
					
						
							|  |  |  |             done, not_done = futures.wait( | 
					
						
							|  |  |  |                     [CANCELLED_FUTURE, future1, future2], | 
					
						
							|  |  |  |                      return_when=futures.FIRST_COMPLETED) | 
					
						
							| 
									
										
										
										
											2023-08-24 19:21:44 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-06 12:30:58 -05:00
										 |  |  |             self.assertEqual(set([future1]), done) | 
					
						
							|  |  |  |             self.assertEqual(set([CANCELLED_FUTURE, future2]), not_done) | 
					
						
							|  |  |  |         finally: | 
					
						
							|  |  |  |             event.set() | 
					
						
							|  |  |  |         future2.result()  # wait for job to finish | 
					
						
							| 
									
										
										
										
											2023-08-24 19:21:44 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_first_completed_some_already_completed(self): | 
					
						
							| 
									
										
										
										
											2025-03-06 12:30:58 -05:00
										 |  |  |         event = self.create_event() | 
					
						
							|  |  |  |         future1 = self.executor.submit(event.wait) | 
					
						
							| 
									
										
										
										
											2023-08-24 19:21:44 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-06 12:30:58 -05:00
										 |  |  |         try: | 
					
						
							|  |  |  |             finished, pending = futures.wait( | 
					
						
							|  |  |  |                      [CANCELLED_AND_NOTIFIED_FUTURE, SUCCESSFUL_FUTURE, future1], | 
					
						
							|  |  |  |                      return_when=futures.FIRST_COMPLETED) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             self.assertEqual( | 
					
						
							|  |  |  |                     set([CANCELLED_AND_NOTIFIED_FUTURE, SUCCESSFUL_FUTURE]), | 
					
						
							|  |  |  |                     finished) | 
					
						
							|  |  |  |             self.assertEqual(set([future1]), pending) | 
					
						
							|  |  |  |         finally: | 
					
						
							|  |  |  |             event.set() | 
					
						
							|  |  |  |         future1.result()  # wait for job to finish | 
					
						
							| 
									
										
										
										
											2023-08-24 19:21:44 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_first_exception(self): | 
					
						
							| 
									
										
										
										
											2025-03-06 12:30:58 -05:00
										 |  |  |         event1 = self.create_event() | 
					
						
							|  |  |  |         event2 = self.create_event() | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             future1 = self.executor.submit(mul, 2, 21) | 
					
						
							|  |  |  |             future2 = self.executor.submit(wait_and_raise, event1) | 
					
						
							|  |  |  |             future3 = self.executor.submit(event2.wait) | 
					
						
							| 
									
										
										
										
											2023-08-24 19:21:44 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-06 12:30:58 -05:00
										 |  |  |             # Ensure that future1 is completed before future2 finishes | 
					
						
							|  |  |  |             def wait_for_future1(): | 
					
						
							|  |  |  |                 future1.result() | 
					
						
							|  |  |  |                 event1.set() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             t = threading.Thread(target=wait_for_future1) | 
					
						
							|  |  |  |             t.start() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             finished, pending = futures.wait( | 
					
						
							|  |  |  |                     [future1, future2, future3], | 
					
						
							|  |  |  |                     return_when=futures.FIRST_EXCEPTION) | 
					
						
							| 
									
										
										
										
											2023-08-24 19:21:44 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-06 12:30:58 -05:00
										 |  |  |             self.assertEqual(set([future1, future2]), finished) | 
					
						
							|  |  |  |             self.assertEqual(set([future3]), pending) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             threading_helper.join_thread(t) | 
					
						
							|  |  |  |         finally: | 
					
						
							|  |  |  |             event1.set() | 
					
						
							|  |  |  |             event2.set() | 
					
						
							|  |  |  |         future3.result()  # wait for job to finish | 
					
						
							| 
									
										
										
										
											2023-08-24 19:21:44 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_first_exception_some_already_complete(self): | 
					
						
							| 
									
										
										
										
											2025-03-06 12:30:58 -05:00
										 |  |  |         event = self.create_event() | 
					
						
							| 
									
										
										
										
											2023-08-24 19:21:44 +02:00
										 |  |  |         future1 = self.executor.submit(divmod, 21, 0) | 
					
						
							| 
									
										
										
										
											2025-03-06 12:30:58 -05:00
										 |  |  |         future2 = self.executor.submit(event.wait) | 
					
						
							| 
									
										
										
										
											2023-08-24 19:21:44 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-06 12:30:58 -05:00
										 |  |  |         try: | 
					
						
							|  |  |  |             finished, pending = futures.wait( | 
					
						
							|  |  |  |                     [SUCCESSFUL_FUTURE, | 
					
						
							|  |  |  |                      CANCELLED_FUTURE, | 
					
						
							|  |  |  |                      CANCELLED_AND_NOTIFIED_FUTURE, | 
					
						
							|  |  |  |                      future1, future2], | 
					
						
							|  |  |  |                     return_when=futures.FIRST_EXCEPTION) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             self.assertEqual(set([SUCCESSFUL_FUTURE, | 
					
						
							|  |  |  |                                   CANCELLED_AND_NOTIFIED_FUTURE, | 
					
						
							|  |  |  |                                   future1]), finished) | 
					
						
							|  |  |  |             self.assertEqual(set([CANCELLED_FUTURE, future2]), pending) | 
					
						
							|  |  |  |         finally: | 
					
						
							|  |  |  |             event.set() | 
					
						
							|  |  |  |         future2.result()  # wait for job to finish | 
					
						
							| 
									
										
										
										
											2023-08-24 19:21:44 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_first_exception_one_already_failed(self): | 
					
						
							| 
									
										
										
										
											2025-03-06 12:30:58 -05:00
										 |  |  |         event = self.create_event() | 
					
						
							|  |  |  |         future1 = self.executor.submit(event.wait) | 
					
						
							| 
									
										
										
										
											2023-08-24 19:21:44 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-06 12:30:58 -05:00
										 |  |  |         try: | 
					
						
							|  |  |  |             finished, pending = futures.wait( | 
					
						
							|  |  |  |                      [EXCEPTION_FUTURE, future1], | 
					
						
							|  |  |  |                      return_when=futures.FIRST_EXCEPTION) | 
					
						
							| 
									
										
										
										
											2023-08-24 19:21:44 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-06 12:30:58 -05:00
										 |  |  |             self.assertEqual(set([EXCEPTION_FUTURE]), finished) | 
					
						
							|  |  |  |             self.assertEqual(set([future1]), pending) | 
					
						
							|  |  |  |         finally: | 
					
						
							|  |  |  |             event.set() | 
					
						
							|  |  |  |         future1.result()  # wait for job to finish | 
					
						
							| 
									
										
										
										
											2023-08-24 19:21:44 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_all_completed(self): | 
					
						
							|  |  |  |         future1 = self.executor.submit(divmod, 2, 0) | 
					
						
							|  |  |  |         future2 = self.executor.submit(mul, 2, 21) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         finished, pending = futures.wait( | 
					
						
							|  |  |  |                 [SUCCESSFUL_FUTURE, | 
					
						
							|  |  |  |                  CANCELLED_AND_NOTIFIED_FUTURE, | 
					
						
							|  |  |  |                  EXCEPTION_FUTURE, | 
					
						
							|  |  |  |                  future1, | 
					
						
							|  |  |  |                  future2], | 
					
						
							|  |  |  |                 return_when=futures.ALL_COMPLETED) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.assertEqual(set([SUCCESSFUL_FUTURE, | 
					
						
							|  |  |  |                               CANCELLED_AND_NOTIFIED_FUTURE, | 
					
						
							|  |  |  |                               EXCEPTION_FUTURE, | 
					
						
							|  |  |  |                               future1, | 
					
						
							|  |  |  |                               future2]), finished) | 
					
						
							|  |  |  |         self.assertEqual(set(), pending) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_timeout(self): | 
					
						
							| 
									
										
										
										
											2023-09-28 15:21:15 +02:00
										 |  |  |         short_timeout = 0.050 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-06 12:30:58 -05:00
										 |  |  |         event = self.create_event() | 
					
						
							|  |  |  |         future = self.executor.submit(event.wait) | 
					
						
							| 
									
										
										
										
											2023-08-24 19:21:44 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-06 12:30:58 -05:00
										 |  |  |         try: | 
					
						
							|  |  |  |             finished, pending = futures.wait( | 
					
						
							|  |  |  |                     [CANCELLED_AND_NOTIFIED_FUTURE, | 
					
						
							|  |  |  |                      EXCEPTION_FUTURE, | 
					
						
							|  |  |  |                      SUCCESSFUL_FUTURE, | 
					
						
							|  |  |  |                      future], | 
					
						
							|  |  |  |                     timeout=short_timeout, | 
					
						
							|  |  |  |                     return_when=futures.ALL_COMPLETED) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             self.assertEqual(set([CANCELLED_AND_NOTIFIED_FUTURE, | 
					
						
							|  |  |  |                                   EXCEPTION_FUTURE, | 
					
						
							|  |  |  |                                   SUCCESSFUL_FUTURE]), | 
					
						
							|  |  |  |                              finished) | 
					
						
							|  |  |  |             self.assertEqual(set([future]), pending) | 
					
						
							|  |  |  |         finally: | 
					
						
							|  |  |  |             event.set() | 
					
						
							|  |  |  |         future.result()  # wait for job to finish | 
					
						
							| 
									
										
										
										
											2023-08-24 19:21:44 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ThreadPoolWaitTests(ThreadPoolMixin, WaitTests, BaseTestCase): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_pending_calls_race(self): | 
					
						
							|  |  |  |         # Issue #14406: multi-threaded race condition when waiting on all | 
					
						
							|  |  |  |         # futures. | 
					
						
							|  |  |  |         event = threading.Event() | 
					
						
							|  |  |  |         def future_func(): | 
					
						
							|  |  |  |             event.wait() | 
					
						
							|  |  |  |         oldswitchinterval = sys.getswitchinterval() | 
					
						
							| 
									
										
										
										
											2024-04-05 21:57:36 +01:00
										 |  |  |         support.setswitchinterval(1e-6) | 
					
						
							| 
									
										
										
										
											2023-08-24 19:21:44 +02:00
										 |  |  |         try: | 
					
						
							|  |  |  |             fs = {self.executor.submit(future_func) for i in range(100)} | 
					
						
							|  |  |  |             event.set() | 
					
						
							|  |  |  |             futures.wait(fs, return_when=futures.ALL_COMPLETED) | 
					
						
							|  |  |  |         finally: | 
					
						
							|  |  |  |             sys.setswitchinterval(oldswitchinterval) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | create_executor_tests(globals(), WaitTests, | 
					
						
							|  |  |  |                       executor_mixins=(ProcessPoolForkMixin, | 
					
						
							|  |  |  |                                        ProcessPoolForkserverMixin, | 
					
						
							|  |  |  |                                        ProcessPoolSpawnMixin)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def setUpModule(): | 
					
						
							|  |  |  |     setup_module() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if __name__ == "__main__": | 
					
						
							|  |  |  |     unittest.main() |