| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  | """
 | 
					
						
							|  |  |  | Various tests for synchronization primitives. | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-29 14:04:40 +03:00
										 |  |  | import gc | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  | import sys | 
					
						
							|  |  |  | import time | 
					
						
							| 
									
										
										
										
											2011-05-30 23:02:52 +02:00
										 |  |  | from _thread import start_new_thread, TIMEOUT_MAX | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  | import threading | 
					
						
							|  |  |  | import unittest | 
					
						
							| 
									
										
										
										
											2015-10-09 00:03:51 -04:00
										 |  |  | import weakref | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | from test import support | 
					
						
							| 
									
										
										
										
											2020-05-28 06:10:27 +08:00
										 |  |  | from test.support import threading_helper | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-22 12:04:36 +02:00
										 |  |  | requires_fork = unittest.skipUnless(support.has_fork_support, | 
					
						
							| 
									
										
										
										
											2020-04-07 23:11:49 +02:00
										 |  |  |                                     "platform doesn't support fork " | 
					
						
							|  |  |  |                                      "(no _at_fork_reinit method)") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  | def wait_threads_blocked(nthread): | 
					
						
							|  |  |  |     # Arbitrary sleep to wait until N threads are blocked, | 
					
						
							|  |  |  |     # like waiting for a lock. | 
					
						
							|  |  |  |     time.sleep(0.010 * nthread) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | class Bunch(object): | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     A bunch of threads. | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  |     def __init__(self, func, nthread, wait_before_exit=False): | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  |         Construct a bunch of `nthread` threads running the same function `func`. | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  |         If `wait_before_exit` is True, the threads won't terminate until | 
					
						
							|  |  |  |         do_finish() is called. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  |         self.func = func | 
					
						
							|  |  |  |         self.nthread = nthread | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  |         self.started = [] | 
					
						
							|  |  |  |         self.finished = [] | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  |         self.exceptions = [] | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  |         self._can_exit = not wait_before_exit | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  |         self._wait_thread = None | 
					
						
							| 
									
										
										
										
											2017-09-14 13:07:24 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  |     def task(self): | 
					
						
							|  |  |  |         tid = threading.get_ident() | 
					
						
							|  |  |  |         self.started.append(tid) | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             self.func() | 
					
						
							|  |  |  |         except BaseException as exc: | 
					
						
							|  |  |  |             self.exceptions.append(exc) | 
					
						
							|  |  |  |         finally: | 
					
						
							|  |  |  |             self.finished.append(tid) | 
					
						
							|  |  |  |             for _ in support.sleeping_retry(support.SHORT_TIMEOUT): | 
					
						
							|  |  |  |                 if self._can_exit: | 
					
						
							|  |  |  |                     break | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __enter__(self): | 
					
						
							|  |  |  |         self._wait_thread = threading_helper.wait_threads_exit(support.SHORT_TIMEOUT) | 
					
						
							|  |  |  |         self._wait_thread.__enter__() | 
					
						
							| 
									
										
										
										
											2017-09-14 13:07:24 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-28 20:38:37 +02:00
										 |  |  |         try: | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  |             for _ in range(self.nthread): | 
					
						
							|  |  |  |                 start_new_thread(self.task, ()) | 
					
						
							| 
									
										
										
										
											2015-03-28 20:38:37 +02:00
										 |  |  |         except: | 
					
						
							|  |  |  |             self._can_exit = True | 
					
						
							|  |  |  |             raise | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  |         for _ in support.sleeping_retry(support.SHORT_TIMEOUT): | 
					
						
							|  |  |  |             if len(self.started) >= self.nthread: | 
					
						
							|  |  |  |                 break | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  |         return self | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __exit__(self, exc_type, exc_value, traceback): | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  |         for _ in support.sleeping_retry(support.SHORT_TIMEOUT): | 
					
						
							|  |  |  |             if len(self.finished) >= self.nthread: | 
					
						
							|  |  |  |                 break | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  |         # Wait until threads completely exit according to _thread._count() | 
					
						
							|  |  |  |         self._wait_thread.__exit__(None, None, None) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Break reference cycle | 
					
						
							|  |  |  |         exceptions = self.exceptions | 
					
						
							|  |  |  |         self.exceptions = None | 
					
						
							|  |  |  |         if exceptions: | 
					
						
							|  |  |  |             raise ExceptionGroup(f"{self.func} threads raised exceptions", | 
					
						
							|  |  |  |                                  exceptions) | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def do_finish(self): | 
					
						
							|  |  |  |         self._can_exit = True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class BaseTestCase(unittest.TestCase): | 
					
						
							|  |  |  |     def setUp(self): | 
					
						
							| 
									
										
										
										
											2020-05-28 06:10:27 +08:00
										 |  |  |         self._threads = threading_helper.threading_setup() | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def tearDown(self): | 
					
						
							| 
									
										
										
										
											2020-05-28 06:10:27 +08:00
										 |  |  |         threading_helper.threading_cleanup(*self._threads) | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  |         support.reap_children() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-04-14 15:44:10 +00:00
										 |  |  |     def assertTimeout(self, actual, expected): | 
					
						
							| 
									
										
										
										
											2018-12-17 09:36:36 +01:00
										 |  |  |         # The waiting and/or time.monotonic() can be imprecise, which | 
					
						
							| 
									
										
										
										
											2010-04-14 15:44:10 +00:00
										 |  |  |         # is why comparing to the expected value would sometimes fail | 
					
						
							|  |  |  |         # (especially under Windows). | 
					
						
							|  |  |  |         self.assertGreaterEqual(actual, expected * 0.6) | 
					
						
							|  |  |  |         # Test nothing insane happened | 
					
						
							|  |  |  |         self.assertLess(actual, expected * 10.0) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | class BaseLockTests(BaseTestCase): | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     Tests for both recursive and non-recursive locks. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  |     def wait_phase(self, phase, expected): | 
					
						
							|  |  |  |         for _ in support.sleeping_retry(support.SHORT_TIMEOUT): | 
					
						
							|  |  |  |             if len(phase) >= expected: | 
					
						
							|  |  |  |                 break | 
					
						
							|  |  |  |         self.assertEqual(len(phase), expected) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  |     def test_constructor(self): | 
					
						
							|  |  |  |         lock = self.locktype() | 
					
						
							|  |  |  |         del lock | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-30 15:54:39 +02:00
										 |  |  |     def test_repr(self): | 
					
						
							|  |  |  |         lock = self.locktype() | 
					
						
							| 
									
										
										
										
											2014-05-25 18:22:35 -07:00
										 |  |  |         self.assertRegex(repr(lock), "<unlocked .* object (.*)?at .*>") | 
					
						
							|  |  |  |         del lock | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_locked_repr(self): | 
					
						
							|  |  |  |         lock = self.locktype() | 
					
						
							|  |  |  |         lock.acquire() | 
					
						
							|  |  |  |         self.assertRegex(repr(lock), "<locked .* object (.*)?at .*>") | 
					
						
							| 
									
										
										
										
											2013-07-30 15:54:39 +02:00
										 |  |  |         del lock | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  |     def test_acquire_destroy(self): | 
					
						
							|  |  |  |         lock = self.locktype() | 
					
						
							|  |  |  |         lock.acquire() | 
					
						
							|  |  |  |         del lock | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_acquire_release(self): | 
					
						
							|  |  |  |         lock = self.locktype() | 
					
						
							|  |  |  |         lock.acquire() | 
					
						
							|  |  |  |         lock.release() | 
					
						
							|  |  |  |         del lock | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_try_acquire(self): | 
					
						
							|  |  |  |         lock = self.locktype() | 
					
						
							|  |  |  |         self.assertTrue(lock.acquire(False)) | 
					
						
							|  |  |  |         lock.release() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_try_acquire_contended(self): | 
					
						
							|  |  |  |         lock = self.locktype() | 
					
						
							|  |  |  |         lock.acquire() | 
					
						
							|  |  |  |         result = [] | 
					
						
							|  |  |  |         def f(): | 
					
						
							|  |  |  |             result.append(lock.acquire(False)) | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  |         with Bunch(f, 1): | 
					
						
							|  |  |  |             pass | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  |         self.assertFalse(result[0]) | 
					
						
							|  |  |  |         lock.release() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_acquire_contended(self): | 
					
						
							|  |  |  |         lock = self.locktype() | 
					
						
							|  |  |  |         lock.acquire() | 
					
						
							|  |  |  |         def f(): | 
					
						
							|  |  |  |             lock.acquire() | 
					
						
							|  |  |  |             lock.release() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  |         N = 5 | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  |         with Bunch(f, N) as bunch: | 
					
						
							|  |  |  |             # Threads block on lock.acquire() | 
					
						
							|  |  |  |             wait_threads_blocked(N) | 
					
						
							|  |  |  |             self.assertEqual(len(bunch.finished), 0) | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  |             # Threads unblocked | 
					
						
							|  |  |  |             lock.release() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.assertEqual(len(bunch.finished), N) | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_with(self): | 
					
						
							|  |  |  |         lock = self.locktype() | 
					
						
							|  |  |  |         def f(): | 
					
						
							|  |  |  |             lock.acquire() | 
					
						
							|  |  |  |             lock.release() | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         def with_lock(err=None): | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  |             with lock: | 
					
						
							|  |  |  |                 if err is not None: | 
					
						
							|  |  |  |                     raise err | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # Acquire the lock, do nothing, with releases the lock | 
					
						
							|  |  |  |         with lock: | 
					
						
							|  |  |  |             pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Check that the lock is unacquired | 
					
						
							|  |  |  |         with Bunch(f, 1): | 
					
						
							|  |  |  |             pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Acquire the lock, raise an exception, with releases the lock | 
					
						
							|  |  |  |         with self.assertRaises(TypeError): | 
					
						
							|  |  |  |             with lock: | 
					
						
							|  |  |  |                 raise TypeError | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Check that the lock is unacquired even if after an exception | 
					
						
							|  |  |  |         # was raised in the previous "with lock:" block | 
					
						
							|  |  |  |         with Bunch(f, 1): | 
					
						
							|  |  |  |             pass | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-09 16:08:16 +00:00
										 |  |  |     def test_thread_leak(self): | 
					
						
							|  |  |  |         # The lock shouldn't leak a Thread instance when used from a foreign | 
					
						
							|  |  |  |         # (non-threading) thread. | 
					
						
							|  |  |  |         lock = self.locktype() | 
					
						
							|  |  |  |         def f(): | 
					
						
							|  |  |  |             lock.acquire() | 
					
						
							|  |  |  |             lock.release() | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-09 16:08:16 +00:00
										 |  |  |         # We run many threads in the hope that existing threads ids won't | 
					
						
							|  |  |  |         # be recycled. | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  |         with Bunch(f, 15): | 
					
						
							|  |  |  |             pass | 
					
						
							| 
									
										
										
										
											2009-11-09 16:08:16 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-04-14 15:44:10 +00:00
										 |  |  |     def test_timeout(self): | 
					
						
							|  |  |  |         lock = self.locktype() | 
					
						
							|  |  |  |         # Can't set timeout if not blocking | 
					
						
							| 
									
										
										
										
											2019-09-01 12:16:51 +03:00
										 |  |  |         self.assertRaises(ValueError, lock.acquire, False, 1) | 
					
						
							| 
									
										
										
										
											2010-04-14 15:44:10 +00:00
										 |  |  |         # Invalid timeout values | 
					
						
							|  |  |  |         self.assertRaises(ValueError, lock.acquire, timeout=-100) | 
					
						
							|  |  |  |         self.assertRaises(OverflowError, lock.acquire, timeout=1e100) | 
					
						
							|  |  |  |         self.assertRaises(OverflowError, lock.acquire, timeout=TIMEOUT_MAX + 1) | 
					
						
							|  |  |  |         # TIMEOUT_MAX is ok | 
					
						
							|  |  |  |         lock.acquire(timeout=TIMEOUT_MAX) | 
					
						
							|  |  |  |         lock.release() | 
					
						
							| 
									
										
										
										
											2018-12-17 09:36:36 +01:00
										 |  |  |         t1 = time.monotonic() | 
					
						
							| 
									
										
										
										
											2010-04-14 15:44:10 +00:00
										 |  |  |         self.assertTrue(lock.acquire(timeout=5)) | 
					
						
							| 
									
										
										
										
											2018-12-17 09:36:36 +01:00
										 |  |  |         t2 = time.monotonic() | 
					
						
							| 
									
										
										
										
											2010-04-14 15:44:10 +00:00
										 |  |  |         # Just a sanity test that it didn't actually wait for the timeout. | 
					
						
							|  |  |  |         self.assertLess(t2 - t1, 5) | 
					
						
							|  |  |  |         results = [] | 
					
						
							|  |  |  |         def f(): | 
					
						
							| 
									
										
										
										
											2018-12-17 09:36:36 +01:00
										 |  |  |             t1 = time.monotonic() | 
					
						
							| 
									
										
										
										
											2010-04-14 15:44:10 +00:00
										 |  |  |             results.append(lock.acquire(timeout=0.5)) | 
					
						
							| 
									
										
										
										
											2018-12-17 09:36:36 +01:00
										 |  |  |             t2 = time.monotonic() | 
					
						
							| 
									
										
										
										
											2010-04-14 15:44:10 +00:00
										 |  |  |             results.append(t2 - t1) | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  |         with Bunch(f, 1): | 
					
						
							|  |  |  |             pass | 
					
						
							| 
									
										
										
										
											2010-04-14 15:44:10 +00:00
										 |  |  |         self.assertFalse(results[0]) | 
					
						
							|  |  |  |         self.assertTimeout(results[1], 0.5) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-09 00:03:51 -04:00
										 |  |  |     def test_weakref_exists(self): | 
					
						
							|  |  |  |         lock = self.locktype() | 
					
						
							|  |  |  |         ref = weakref.ref(lock) | 
					
						
							|  |  |  |         self.assertIsNotNone(ref()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_weakref_deleted(self): | 
					
						
							|  |  |  |         lock = self.locktype() | 
					
						
							|  |  |  |         ref = weakref.ref(lock) | 
					
						
							|  |  |  |         del lock | 
					
						
							| 
									
										
										
										
											2021-08-29 14:04:40 +03:00
										 |  |  |         gc.collect()  # For PyPy or other GCs. | 
					
						
							| 
									
										
										
										
											2015-10-09 00:03:51 -04:00
										 |  |  |         self.assertIsNone(ref()) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | class LockTests(BaseLockTests): | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     Tests for non-recursive, weak locks | 
					
						
							|  |  |  |     (which can be acquired and released from different threads). | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     def test_reacquire(self): | 
					
						
							|  |  |  |         # Lock needs to be released before re-acquiring. | 
					
						
							|  |  |  |         lock = self.locktype() | 
					
						
							|  |  |  |         phase = [] | 
					
						
							| 
									
										
										
										
											2017-09-14 13:07:24 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  |         def f(): | 
					
						
							|  |  |  |             lock.acquire() | 
					
						
							|  |  |  |             phase.append(None) | 
					
						
							|  |  |  |             lock.acquire() | 
					
						
							|  |  |  |             phase.append(None) | 
					
						
							| 
									
										
										
										
											2017-09-14 13:07:24 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-28 06:10:27 +08:00
										 |  |  |         with threading_helper.wait_threads_exit(): | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  |             # Thread blocked on lock.acquire() | 
					
						
							| 
									
										
										
										
											2017-09-14 13:07:24 -07:00
										 |  |  |             start_new_thread(f, ()) | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  |             self.wait_phase(phase, 1) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # Thread unblocked | 
					
						
							| 
									
										
										
										
											2017-09-14 13:07:24 -07:00
										 |  |  |             lock.release() | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  |             self.wait_phase(phase, 2) | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_different_thread(self): | 
					
						
							|  |  |  |         # Lock can be released from a different thread. | 
					
						
							|  |  |  |         lock = self.locktype() | 
					
						
							|  |  |  |         lock.acquire() | 
					
						
							|  |  |  |         def f(): | 
					
						
							|  |  |  |             lock.release() | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  |         with Bunch(f, 1): | 
					
						
							|  |  |  |             pass | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  |         lock.acquire() | 
					
						
							|  |  |  |         lock.release() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-03-31 01:00:32 +02:00
										 |  |  |     def test_state_after_timeout(self): | 
					
						
							|  |  |  |         # Issue #11618: check that lock is in a proper state after a | 
					
						
							|  |  |  |         # (non-zero) timeout. | 
					
						
							|  |  |  |         lock = self.locktype() | 
					
						
							|  |  |  |         lock.acquire() | 
					
						
							|  |  |  |         self.assertFalse(lock.acquire(timeout=0.01)) | 
					
						
							|  |  |  |         lock.release() | 
					
						
							|  |  |  |         self.assertFalse(lock.locked()) | 
					
						
							|  |  |  |         self.assertTrue(lock.acquire(blocking=False)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-07 23:11:49 +02:00
										 |  |  |     @requires_fork | 
					
						
							|  |  |  |     def test_at_fork_reinit(self): | 
					
						
							|  |  |  |         def use_lock(lock): | 
					
						
							|  |  |  |             # make sure that the lock still works normally | 
					
						
							|  |  |  |             # after _at_fork_reinit() | 
					
						
							|  |  |  |             lock.acquire() | 
					
						
							|  |  |  |             lock.release() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # unlocked | 
					
						
							|  |  |  |         lock = self.locktype() | 
					
						
							|  |  |  |         lock._at_fork_reinit() | 
					
						
							|  |  |  |         use_lock(lock) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # locked: _at_fork_reinit() resets the lock to the unlocked state | 
					
						
							|  |  |  |         lock2 = self.locktype() | 
					
						
							|  |  |  |         lock2.acquire() | 
					
						
							|  |  |  |         lock2._at_fork_reinit() | 
					
						
							|  |  |  |         use_lock(lock2) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | class RLockTests(BaseLockTests): | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     Tests for recursive locks. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     def test_reacquire(self): | 
					
						
							|  |  |  |         lock = self.locktype() | 
					
						
							|  |  |  |         lock.acquire() | 
					
						
							|  |  |  |         lock.acquire() | 
					
						
							|  |  |  |         lock.release() | 
					
						
							|  |  |  |         lock.acquire() | 
					
						
							|  |  |  |         lock.release() | 
					
						
							|  |  |  |         lock.release() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_release_unacquired(self): | 
					
						
							|  |  |  |         # Cannot release an unacquired lock | 
					
						
							|  |  |  |         lock = self.locktype() | 
					
						
							|  |  |  |         self.assertRaises(RuntimeError, lock.release) | 
					
						
							|  |  |  |         lock.acquire() | 
					
						
							|  |  |  |         lock.acquire() | 
					
						
							|  |  |  |         lock.release() | 
					
						
							|  |  |  |         lock.acquire() | 
					
						
							|  |  |  |         lock.release() | 
					
						
							|  |  |  |         lock.release() | 
					
						
							|  |  |  |         self.assertRaises(RuntimeError, lock.release) | 
					
						
							| 
									
										
										
										
											2012-05-17 18:55:59 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_release_save_unacquired(self): | 
					
						
							|  |  |  |         # Cannot _release_save an unacquired lock | 
					
						
							|  |  |  |         lock = self.locktype() | 
					
						
							|  |  |  |         self.assertRaises(RuntimeError, lock._release_save) | 
					
						
							|  |  |  |         lock.acquire() | 
					
						
							|  |  |  |         lock.acquire() | 
					
						
							|  |  |  |         lock.release() | 
					
						
							|  |  |  |         lock.acquire() | 
					
						
							|  |  |  |         lock.release() | 
					
						
							|  |  |  |         lock.release() | 
					
						
							| 
									
										
										
										
											2011-04-24 23:41:33 +02:00
										 |  |  |         self.assertRaises(RuntimeError, lock._release_save) | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-26 13:57:25 +02:00
										 |  |  |     def test_recursion_count(self): | 
					
						
							|  |  |  |         lock = self.locktype() | 
					
						
							|  |  |  |         self.assertEqual(0, lock._recursion_count()) | 
					
						
							|  |  |  |         lock.acquire() | 
					
						
							|  |  |  |         self.assertEqual(1, lock._recursion_count()) | 
					
						
							|  |  |  |         lock.acquire() | 
					
						
							|  |  |  |         lock.acquire() | 
					
						
							|  |  |  |         self.assertEqual(3, lock._recursion_count()) | 
					
						
							|  |  |  |         lock.release() | 
					
						
							|  |  |  |         self.assertEqual(2, lock._recursion_count()) | 
					
						
							|  |  |  |         lock.release() | 
					
						
							|  |  |  |         lock.release() | 
					
						
							|  |  |  |         self.assertEqual(0, lock._recursion_count()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         phase = [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def f(): | 
					
						
							|  |  |  |             lock.acquire() | 
					
						
							|  |  |  |             phase.append(None) | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |             self.wait_phase(phase, 2) | 
					
						
							| 
									
										
										
										
											2023-09-26 13:57:25 +02:00
										 |  |  |             lock.release() | 
					
						
							|  |  |  |             phase.append(None) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         with threading_helper.wait_threads_exit(): | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  |             # Thread blocked on lock.acquire() | 
					
						
							| 
									
										
										
										
											2023-09-26 13:57:25 +02:00
										 |  |  |             start_new_thread(f, ()) | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  |             self.wait_phase(phase, 1) | 
					
						
							| 
									
										
										
										
											2023-09-26 13:57:25 +02:00
										 |  |  |             self.assertEqual(0, lock._recursion_count()) | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |             # Thread unblocked | 
					
						
							| 
									
										
										
										
											2023-09-26 13:57:25 +02:00
										 |  |  |             phase.append(None) | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  |             self.wait_phase(phase, 3) | 
					
						
							| 
									
										
										
										
											2023-09-26 13:57:25 +02:00
										 |  |  |             self.assertEqual(0, lock._recursion_count()) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  |     def test_different_thread(self): | 
					
						
							|  |  |  |         # Cannot release from a different thread | 
					
						
							|  |  |  |         lock = self.locktype() | 
					
						
							|  |  |  |         def f(): | 
					
						
							|  |  |  |             lock.acquire() | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         with Bunch(f, 1, True) as bunch: | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 self.assertRaises(RuntimeError, lock.release) | 
					
						
							|  |  |  |             finally: | 
					
						
							|  |  |  |                 bunch.do_finish() | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test__is_owned(self): | 
					
						
							|  |  |  |         lock = self.locktype() | 
					
						
							|  |  |  |         self.assertFalse(lock._is_owned()) | 
					
						
							|  |  |  |         lock.acquire() | 
					
						
							|  |  |  |         self.assertTrue(lock._is_owned()) | 
					
						
							|  |  |  |         lock.acquire() | 
					
						
							|  |  |  |         self.assertTrue(lock._is_owned()) | 
					
						
							|  |  |  |         result = [] | 
					
						
							|  |  |  |         def f(): | 
					
						
							|  |  |  |             result.append(lock._is_owned()) | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  |         with Bunch(f, 1): | 
					
						
							|  |  |  |             pass | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  |         self.assertFalse(result[0]) | 
					
						
							|  |  |  |         lock.release() | 
					
						
							|  |  |  |         self.assertTrue(lock._is_owned()) | 
					
						
							|  |  |  |         lock.release() | 
					
						
							|  |  |  |         self.assertFalse(lock._is_owned()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class EventTests(BaseTestCase): | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     Tests for Event objects. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_is_set(self): | 
					
						
							|  |  |  |         evt = self.eventtype() | 
					
						
							|  |  |  |         self.assertFalse(evt.is_set()) | 
					
						
							|  |  |  |         evt.set() | 
					
						
							|  |  |  |         self.assertTrue(evt.is_set()) | 
					
						
							|  |  |  |         evt.set() | 
					
						
							|  |  |  |         self.assertTrue(evt.is_set()) | 
					
						
							|  |  |  |         evt.clear() | 
					
						
							|  |  |  |         self.assertFalse(evt.is_set()) | 
					
						
							|  |  |  |         evt.clear() | 
					
						
							|  |  |  |         self.assertFalse(evt.is_set()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _check_notify(self, evt): | 
					
						
							|  |  |  |         # All threads get notified | 
					
						
							|  |  |  |         N = 5 | 
					
						
							|  |  |  |         results1 = [] | 
					
						
							|  |  |  |         results2 = [] | 
					
						
							|  |  |  |         def f(): | 
					
						
							|  |  |  |             results1.append(evt.wait()) | 
					
						
							|  |  |  |             results2.append(evt.wait()) | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  |         with Bunch(f, N): | 
					
						
							|  |  |  |             # Threads blocked on first evt.wait() | 
					
						
							|  |  |  |             wait_threads_blocked(N) | 
					
						
							|  |  |  |             self.assertEqual(len(results1), 0) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # Threads unblocked | 
					
						
							|  |  |  |             evt.set() | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  |         self.assertEqual(results1, [True] * N) | 
					
						
							|  |  |  |         self.assertEqual(results2, [True] * N) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_notify(self): | 
					
						
							|  |  |  |         evt = self.eventtype() | 
					
						
							|  |  |  |         self._check_notify(evt) | 
					
						
							|  |  |  |         # Another time, after an explicit clear() | 
					
						
							|  |  |  |         evt.set() | 
					
						
							|  |  |  |         evt.clear() | 
					
						
							|  |  |  |         self._check_notify(evt) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_timeout(self): | 
					
						
							|  |  |  |         evt = self.eventtype() | 
					
						
							|  |  |  |         results1 = [] | 
					
						
							|  |  |  |         results2 = [] | 
					
						
							|  |  |  |         N = 5 | 
					
						
							|  |  |  |         def f(): | 
					
						
							|  |  |  |             results1.append(evt.wait(0.0)) | 
					
						
							| 
									
										
										
										
											2018-12-17 09:36:36 +01:00
										 |  |  |             t1 = time.monotonic() | 
					
						
							| 
									
										
										
										
											2010-04-14 15:44:10 +00:00
										 |  |  |             r = evt.wait(0.5) | 
					
						
							| 
									
										
										
										
											2018-12-17 09:36:36 +01:00
										 |  |  |             t2 = time.monotonic() | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  |             results2.append((r, t2 - t1)) | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         with Bunch(f, N): | 
					
						
							|  |  |  |             pass | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  |         self.assertEqual(results1, [False] * N) | 
					
						
							|  |  |  |         for r, dt in results2: | 
					
						
							|  |  |  |             self.assertFalse(r) | 
					
						
							| 
									
										
										
										
											2010-04-14 15:44:10 +00:00
										 |  |  |             self.assertTimeout(dt, 0.5) | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  |         # The event is set | 
					
						
							|  |  |  |         results1 = [] | 
					
						
							|  |  |  |         results2 = [] | 
					
						
							|  |  |  |         evt.set() | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  |         with Bunch(f, N): | 
					
						
							|  |  |  |             pass | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  |         self.assertEqual(results1, [True] * N) | 
					
						
							|  |  |  |         for r, dt in results2: | 
					
						
							|  |  |  |             self.assertTrue(r) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-01-07 18:24:56 +01:00
										 |  |  |     def test_set_and_clear(self): | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  |         # gh-57711: check that wait() returns true even when the event is | 
					
						
							| 
									
										
										
										
											2012-01-07 18:24:56 +01:00
										 |  |  |         # cleared before the waiting thread is woken up. | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  |         event = self.eventtype() | 
					
						
							| 
									
										
										
										
											2012-01-07 18:24:56 +01:00
										 |  |  |         results = [] | 
					
						
							|  |  |  |         def f(): | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  |             results.append(event.wait(support.LONG_TIMEOUT)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         N = 5 | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  |         with Bunch(f, N): | 
					
						
							|  |  |  |             # Threads blocked on event.wait() | 
					
						
							|  |  |  |             wait_threads_blocked(N) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # Threads unblocked | 
					
						
							|  |  |  |             event.set() | 
					
						
							|  |  |  |             event.clear() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-01-07 18:24:56 +01:00
										 |  |  |         self.assertEqual(results, [True] * N) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-07 23:11:49 +02:00
										 |  |  |     @requires_fork | 
					
						
							|  |  |  |     def test_at_fork_reinit(self): | 
					
						
							| 
									
										
										
										
											2016-04-29 17:25:29 +03:00
										 |  |  |         # ensure that condition is still using a Lock after reset | 
					
						
							| 
									
										
										
										
											2015-10-05 21:56:22 -07:00
										 |  |  |         evt = self.eventtype() | 
					
						
							| 
									
										
										
										
											2016-04-29 17:25:29 +03:00
										 |  |  |         with evt._cond: | 
					
						
							|  |  |  |             self.assertFalse(evt._cond.acquire(False)) | 
					
						
							| 
									
										
										
										
											2020-04-07 23:11:49 +02:00
										 |  |  |         evt._at_fork_reinit() | 
					
						
							| 
									
										
										
										
											2016-04-29 17:25:29 +03:00
										 |  |  |         with evt._cond: | 
					
						
							|  |  |  |             self.assertFalse(evt._cond.acquire(False)) | 
					
						
							| 
									
										
										
										
											2015-10-05 21:56:22 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-29 13:07:58 +03:00
										 |  |  |     def test_repr(self): | 
					
						
							|  |  |  |         evt = self.eventtype() | 
					
						
							|  |  |  |         self.assertRegex(repr(evt), r"<\w+\.Event at .*: unset>") | 
					
						
							|  |  |  |         evt.set() | 
					
						
							|  |  |  |         self.assertRegex(repr(evt), r"<\w+\.Event at .*: set>") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | class ConditionTests(BaseTestCase): | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     Tests for condition variables. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_acquire(self): | 
					
						
							|  |  |  |         cond = self.condtype() | 
					
						
							|  |  |  |         # Be default we have an RLock: the condition can be acquired multiple | 
					
						
							|  |  |  |         # times. | 
					
						
							|  |  |  |         cond.acquire() | 
					
						
							|  |  |  |         cond.acquire() | 
					
						
							|  |  |  |         cond.release() | 
					
						
							|  |  |  |         cond.release() | 
					
						
							|  |  |  |         lock = threading.Lock() | 
					
						
							|  |  |  |         cond = self.condtype(lock) | 
					
						
							|  |  |  |         cond.acquire() | 
					
						
							|  |  |  |         self.assertFalse(lock.acquire(False)) | 
					
						
							|  |  |  |         cond.release() | 
					
						
							|  |  |  |         self.assertTrue(lock.acquire(False)) | 
					
						
							|  |  |  |         self.assertFalse(cond.acquire(False)) | 
					
						
							|  |  |  |         lock.release() | 
					
						
							|  |  |  |         with cond: | 
					
						
							|  |  |  |             self.assertFalse(lock.acquire(False)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_unacquired_wait(self): | 
					
						
							|  |  |  |         cond = self.condtype() | 
					
						
							|  |  |  |         self.assertRaises(RuntimeError, cond.wait) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_unacquired_notify(self): | 
					
						
							|  |  |  |         cond = self.condtype() | 
					
						
							|  |  |  |         self.assertRaises(RuntimeError, cond.notify) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _check_notify(self, cond): | 
					
						
							| 
									
										
										
										
											2013-11-11 11:29:04 +00:00
										 |  |  |         # Note that this test is sensitive to timing.  If the worker threads | 
					
						
							|  |  |  |         # don't execute in a timely fashion, the main thread may think they | 
					
						
							|  |  |  |         # are further along then they are.  The main thread therefore issues | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  |         # wait_threads_blocked() statements to try to make sure that it doesn't | 
					
						
							|  |  |  |         # race ahead of the workers. | 
					
						
							| 
									
										
										
										
											2013-11-11 11:29:04 +00:00
										 |  |  |         # Secondly, this test assumes that condition variables are not subject | 
					
						
							|  |  |  |         # to spurious wakeups.  The absence of spurious wakeups is an implementation | 
					
						
							| 
									
										
										
										
											2019-08-31 06:21:19 +10:00
										 |  |  |         # detail of Condition Variables in current CPython, but in general, not | 
					
						
							| 
									
										
										
										
											2013-11-11 11:29:04 +00:00
										 |  |  |         # a guaranteed property of condition variables as a programming | 
					
						
							|  |  |  |         # construct.  In particular, it is possible that this can no longer | 
					
						
							|  |  |  |         # be conveniently guaranteed should their implementation ever change. | 
					
						
							| 
									
										
										
										
											2017-06-23 13:36:36 +03:00
										 |  |  |         ready = [] | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  |         results1 = [] | 
					
						
							|  |  |  |         results2 = [] | 
					
						
							|  |  |  |         phase_num = 0 | 
					
						
							|  |  |  |         def f(): | 
					
						
							|  |  |  |             cond.acquire() | 
					
						
							| 
									
										
										
										
											2017-06-23 13:36:36 +03:00
										 |  |  |             ready.append(phase_num) | 
					
						
							| 
									
										
										
										
											2010-10-28 09:03:20 +00:00
										 |  |  |             result = cond.wait() | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  |             cond.release() | 
					
						
							| 
									
										
										
										
											2010-10-28 09:03:20 +00:00
										 |  |  |             results1.append((result, phase_num)) | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  |             cond.acquire() | 
					
						
							| 
									
										
										
										
											2017-06-23 13:36:36 +03:00
										 |  |  |             ready.append(phase_num) | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-28 09:03:20 +00:00
										 |  |  |             result = cond.wait() | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  |             cond.release() | 
					
						
							| 
									
										
										
										
											2010-10-28 09:03:20 +00:00
										 |  |  |             results2.append((result, phase_num)) | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         N = 5 | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  |         with Bunch(f, N): | 
					
						
							|  |  |  |             # first wait, to ensure all workers settle into cond.wait() before | 
					
						
							|  |  |  |             # we continue. See issues #8799 and #30727. | 
					
						
							|  |  |  |             for _ in support.sleeping_retry(support.SHORT_TIMEOUT): | 
					
						
							|  |  |  |                 if len(ready) >= N: | 
					
						
							|  |  |  |                     break | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  |             ready.clear() | 
					
						
							|  |  |  |             self.assertEqual(results1, []) | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  |             # Notify 3 threads at first | 
					
						
							|  |  |  |             count1 = 3 | 
					
						
							|  |  |  |             cond.acquire() | 
					
						
							|  |  |  |             cond.notify(count1) | 
					
						
							|  |  |  |             wait_threads_blocked(count1) | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  |             # Phase 1 | 
					
						
							|  |  |  |             phase_num = 1 | 
					
						
							|  |  |  |             cond.release() | 
					
						
							|  |  |  |             for _ in support.sleeping_retry(support.SHORT_TIMEOUT): | 
					
						
							|  |  |  |                 if len(results1) >= count1: | 
					
						
							|  |  |  |                     break | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  |             self.assertEqual(results1, [(True, 1)] * count1) | 
					
						
							|  |  |  |             self.assertEqual(results2, []) | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  |             # Wait until awaken workers are blocked on cond.wait() | 
					
						
							|  |  |  |             for _ in support.sleeping_retry(support.SHORT_TIMEOUT): | 
					
						
							|  |  |  |                 if len(ready) >= count1 : | 
					
						
							|  |  |  |                     break | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  |             # Notify 5 threads: they might be in their first or second wait | 
					
						
							|  |  |  |             cond.acquire() | 
					
						
							|  |  |  |             cond.notify(5) | 
					
						
							|  |  |  |             wait_threads_blocked(N) | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  |             # Phase 2 | 
					
						
							|  |  |  |             phase_num = 2 | 
					
						
							|  |  |  |             cond.release() | 
					
						
							|  |  |  |             for _ in support.sleeping_retry(support.SHORT_TIMEOUT): | 
					
						
							|  |  |  |                 if len(results1) + len(results2) >= (N + count1): | 
					
						
							|  |  |  |                     break | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  |             count2 = N - count1 | 
					
						
							|  |  |  |             self.assertEqual(results1, [(True, 1)] * count1 + [(True, 2)] * count2) | 
					
						
							|  |  |  |             self.assertEqual(results2, [(True, 2)] * count1) | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  |             # Make sure all workers settle into cond.wait() | 
					
						
							|  |  |  |             for _ in support.sleeping_retry(support.SHORT_TIMEOUT): | 
					
						
							|  |  |  |                 if len(ready) >= N: | 
					
						
							|  |  |  |                     break | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  |             # Notify all threads: they are all in their second wait | 
					
						
							|  |  |  |             cond.acquire() | 
					
						
							|  |  |  |             cond.notify_all() | 
					
						
							|  |  |  |             wait_threads_blocked(N) | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  |             # Phase 3 | 
					
						
							|  |  |  |             phase_num = 3 | 
					
						
							|  |  |  |             cond.release() | 
					
						
							|  |  |  |             for _ in support.sleeping_retry(support.SHORT_TIMEOUT): | 
					
						
							|  |  |  |                 if len(results2) >= N: | 
					
						
							|  |  |  |                     break | 
					
						
							|  |  |  |             self.assertEqual(results1, [(True, 1)] * count1 + [(True, 2)] * count2) | 
					
						
							|  |  |  |             self.assertEqual(results2, [(True, 2)] * count1 + [(True, 3)] * count2) | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_notify(self): | 
					
						
							|  |  |  |         cond = self.condtype() | 
					
						
							|  |  |  |         self._check_notify(cond) | 
					
						
							|  |  |  |         # A second time, to check internal state is still ok. | 
					
						
							|  |  |  |         self._check_notify(cond) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_timeout(self): | 
					
						
							|  |  |  |         cond = self.condtype() | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  |         timeout = 0.5 | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  |         results = [] | 
					
						
							|  |  |  |         def f(): | 
					
						
							|  |  |  |             cond.acquire() | 
					
						
							| 
									
										
										
										
											2018-12-17 09:36:36 +01:00
										 |  |  |             t1 = time.monotonic() | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  |             result = cond.wait(timeout) | 
					
						
							| 
									
										
										
										
											2018-12-17 09:36:36 +01:00
										 |  |  |             t2 = time.monotonic() | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  |             cond.release() | 
					
						
							| 
									
										
										
										
											2010-10-28 09:03:20 +00:00
										 |  |  |             results.append((t2 - t1, result)) | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         N = 5 | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  |         with Bunch(f, N): | 
					
						
							|  |  |  |             pass | 
					
						
							| 
									
										
										
										
											2010-10-28 09:03:20 +00:00
										 |  |  |         self.assertEqual(len(results), N) | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-28 09:03:20 +00:00
										 |  |  |         for dt, result in results: | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  |             self.assertTimeout(dt, timeout) | 
					
						
							| 
									
										
										
										
											2010-10-28 09:03:20 +00:00
										 |  |  |             # Note that conceptually (that"s the condition variable protocol) | 
					
						
							|  |  |  |             # a wait() may succeed even if no one notifies us and before any | 
					
						
							|  |  |  |             # timeout occurs.  Spurious wakeups can occur. | 
					
						
							|  |  |  |             # This makes it hard to verify the result value. | 
					
						
							|  |  |  |             # In practice, this implementation has no spurious wakeups. | 
					
						
							|  |  |  |             self.assertFalse(result) | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-11-18 12:46:39 +00:00
										 |  |  |     def test_waitfor(self): | 
					
						
							|  |  |  |         cond = self.condtype() | 
					
						
							|  |  |  |         state = 0 | 
					
						
							|  |  |  |         def f(): | 
					
						
							|  |  |  |             with cond: | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  |                 result = cond.wait_for(lambda: state == 4) | 
					
						
							| 
									
										
										
										
											2010-11-18 12:46:39 +00:00
										 |  |  |                 self.assertTrue(result) | 
					
						
							|  |  |  |                 self.assertEqual(state, 4) | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         with Bunch(f, 1): | 
					
						
							|  |  |  |             for i in range(4): | 
					
						
							|  |  |  |                 time.sleep(0.010) | 
					
						
							|  |  |  |                 with cond: | 
					
						
							|  |  |  |                     state += 1 | 
					
						
							|  |  |  |                     cond.notify() | 
					
						
							| 
									
										
										
										
											2010-11-18 12:46:39 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_waitfor_timeout(self): | 
					
						
							|  |  |  |         cond = self.condtype() | 
					
						
							|  |  |  |         state = 0 | 
					
						
							|  |  |  |         success = [] | 
					
						
							|  |  |  |         def f(): | 
					
						
							|  |  |  |             with cond: | 
					
						
							| 
									
										
										
										
											2018-12-17 09:36:36 +01:00
										 |  |  |                 dt = time.monotonic() | 
					
						
							| 
									
										
										
										
											2010-11-18 12:46:39 +00:00
										 |  |  |                 result = cond.wait_for(lambda : state==4, timeout=0.1) | 
					
						
							| 
									
										
										
										
											2018-12-17 09:36:36 +01:00
										 |  |  |                 dt = time.monotonic() - dt | 
					
						
							| 
									
										
										
										
											2010-11-18 12:46:39 +00:00
										 |  |  |                 self.assertFalse(result) | 
					
						
							|  |  |  |                 self.assertTimeout(dt, 0.1) | 
					
						
							|  |  |  |                 success.append(None) | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  |         with Bunch(f, 1): | 
					
						
							|  |  |  |             # Only increment 3 times, so state == 4 is never reached. | 
					
						
							|  |  |  |             for i in range(3): | 
					
						
							|  |  |  |                 time.sleep(0.010) | 
					
						
							|  |  |  |                 with cond: | 
					
						
							|  |  |  |                     state += 1 | 
					
						
							|  |  |  |                     cond.notify() | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-11-18 12:46:39 +00:00
										 |  |  |         self.assertEqual(len(success), 1) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | class BaseSemaphoreTests(BaseTestCase): | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     Common tests for {bounded, unbounded} semaphore objects. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_constructor(self): | 
					
						
							|  |  |  |         self.assertRaises(ValueError, self.semtype, value = -1) | 
					
						
							|  |  |  |         self.assertRaises(ValueError, self.semtype, value = -sys.maxsize) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_acquire(self): | 
					
						
							|  |  |  |         sem = self.semtype(1) | 
					
						
							|  |  |  |         sem.acquire() | 
					
						
							|  |  |  |         sem.release() | 
					
						
							|  |  |  |         sem = self.semtype(2) | 
					
						
							|  |  |  |         sem.acquire() | 
					
						
							|  |  |  |         sem.acquire() | 
					
						
							|  |  |  |         sem.release() | 
					
						
							|  |  |  |         sem.release() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_acquire_destroy(self): | 
					
						
							|  |  |  |         sem = self.semtype() | 
					
						
							|  |  |  |         sem.acquire() | 
					
						
							|  |  |  |         del sem | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_acquire_contended(self): | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  |         sem_value = 7 | 
					
						
							|  |  |  |         sem = self.semtype(sem_value) | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  |         sem.acquire() | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-07 11:04:26 -07:00
										 |  |  |         sem_results = [] | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  |         results1 = [] | 
					
						
							|  |  |  |         results2 = [] | 
					
						
							|  |  |  |         phase_num = 0 | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         def func(): | 
					
						
							| 
									
										
										
										
											2017-12-07 11:04:26 -07:00
										 |  |  |             sem_results.append(sem.acquire()) | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  |             results1.append(phase_num) | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-07 11:04:26 -07:00
										 |  |  |             sem_results.append(sem.acquire()) | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  |             results2.append(phase_num) | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         def wait_count(count): | 
					
						
							|  |  |  |             for _ in support.sleeping_retry(support.SHORT_TIMEOUT): | 
					
						
							|  |  |  |                 if len(results1) + len(results2) >= count: | 
					
						
							|  |  |  |                     break | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         N = 10 | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  |         with Bunch(func, N): | 
					
						
							|  |  |  |             # Phase 0 | 
					
						
							|  |  |  |             count1 = sem_value - 1 | 
					
						
							|  |  |  |             wait_count(count1) | 
					
						
							|  |  |  |             self.assertEqual(results1 + results2, [0] * count1) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # Phase 1 | 
					
						
							|  |  |  |             phase_num = 1 | 
					
						
							|  |  |  |             for i in range(sem_value): | 
					
						
							|  |  |  |                 sem.release() | 
					
						
							|  |  |  |             count2 = sem_value | 
					
						
							|  |  |  |             wait_count(count1 + count2) | 
					
						
							|  |  |  |             self.assertEqual(sorted(results1 + results2), | 
					
						
							|  |  |  |                              [0] * count1 + [1] * count2) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # Phase 2 | 
					
						
							|  |  |  |             phase_num = 2 | 
					
						
							|  |  |  |             count3 = (sem_value - 1) | 
					
						
							|  |  |  |             for i in range(count3): | 
					
						
							|  |  |  |                 sem.release() | 
					
						
							|  |  |  |             wait_count(count1 + count2 + count3) | 
					
						
							|  |  |  |             self.assertEqual(sorted(results1 + results2), | 
					
						
							|  |  |  |                              [0] * count1 + [1] * count2 + [2] * count3) | 
					
						
							|  |  |  |             # The semaphore is still locked | 
					
						
							|  |  |  |             self.assertFalse(sem.acquire(False)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # Final release, to let the last thread finish | 
					
						
							|  |  |  |             count4 = 1 | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  |             sem.release() | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         self.assertEqual(sem_results, | 
					
						
							|  |  |  |                          [True] * (count1 + count2 + count3 + count4)) | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-29 01:45:19 -07:00
										 |  |  |     def test_multirelease(self): | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  |         sem_value = 7 | 
					
						
							|  |  |  |         sem = self.semtype(sem_value) | 
					
						
							| 
									
										
										
										
											2019-08-29 01:45:19 -07:00
										 |  |  |         sem.acquire() | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-29 01:45:19 -07:00
										 |  |  |         results1 = [] | 
					
						
							|  |  |  |         results2 = [] | 
					
						
							|  |  |  |         phase_num = 0 | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  |         def func(): | 
					
						
							| 
									
										
										
										
											2019-08-29 01:45:19 -07:00
										 |  |  |             sem.acquire() | 
					
						
							|  |  |  |             results1.append(phase_num) | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-29 01:45:19 -07:00
										 |  |  |             sem.acquire() | 
					
						
							|  |  |  |             results2.append(phase_num) | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         def wait_count(count): | 
					
						
							|  |  |  |             for _ in support.sleeping_retry(support.SHORT_TIMEOUT): | 
					
						
							|  |  |  |                 if len(results1) + len(results2) >= count: | 
					
						
							|  |  |  |                     break | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  |         with Bunch(func, 10): | 
					
						
							|  |  |  |             # Phase 0 | 
					
						
							|  |  |  |             count1 = sem_value - 1 | 
					
						
							|  |  |  |             wait_count(count1) | 
					
						
							|  |  |  |             self.assertEqual(results1 + results2, [0] * count1) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # Phase 1 | 
					
						
							|  |  |  |             phase_num = 1 | 
					
						
							|  |  |  |             count2 = sem_value | 
					
						
							|  |  |  |             sem.release(count2) | 
					
						
							|  |  |  |             wait_count(count1 + count2) | 
					
						
							|  |  |  |             self.assertEqual(sorted(results1 + results2), | 
					
						
							|  |  |  |                              [0] * count1 + [1] * count2) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # Phase 2 | 
					
						
							|  |  |  |             phase_num = 2 | 
					
						
							|  |  |  |             count3 = sem_value - 1 | 
					
						
							|  |  |  |             sem.release(count3) | 
					
						
							|  |  |  |             wait_count(count1 + count2 + count3) | 
					
						
							|  |  |  |             self.assertEqual(sorted(results1 + results2), | 
					
						
							|  |  |  |                              [0] * count1 + [1] * count2 + [2] * count3) | 
					
						
							|  |  |  |             # The semaphore is still locked | 
					
						
							|  |  |  |             self.assertFalse(sem.acquire(False)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # Final release, to let the last thread finish | 
					
						
							|  |  |  |             sem.release() | 
					
						
							| 
									
										
										
										
											2019-08-29 01:45:19 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  |     def test_try_acquire(self): | 
					
						
							|  |  |  |         sem = self.semtype(2) | 
					
						
							|  |  |  |         self.assertTrue(sem.acquire(False)) | 
					
						
							|  |  |  |         self.assertTrue(sem.acquire(False)) | 
					
						
							|  |  |  |         self.assertFalse(sem.acquire(False)) | 
					
						
							|  |  |  |         sem.release() | 
					
						
							|  |  |  |         self.assertTrue(sem.acquire(False)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_try_acquire_contended(self): | 
					
						
							|  |  |  |         sem = self.semtype(4) | 
					
						
							|  |  |  |         sem.acquire() | 
					
						
							|  |  |  |         results = [] | 
					
						
							|  |  |  |         def f(): | 
					
						
							|  |  |  |             results.append(sem.acquire(False)) | 
					
						
							|  |  |  |             results.append(sem.acquire(False)) | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  |         with Bunch(f, 5): | 
					
						
							|  |  |  |             pass | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  |         # There can be a thread switch between acquiring the semaphore and | 
					
						
							|  |  |  |         # appending the result, therefore results will not necessarily be | 
					
						
							|  |  |  |         # ordered. | 
					
						
							|  |  |  |         self.assertEqual(sorted(results), [False] * 7 + [True] *  3 ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-04-17 23:51:58 +00:00
										 |  |  |     def test_acquire_timeout(self): | 
					
						
							|  |  |  |         sem = self.semtype(2) | 
					
						
							|  |  |  |         self.assertRaises(ValueError, sem.acquire, False, timeout=1.0) | 
					
						
							|  |  |  |         self.assertTrue(sem.acquire(timeout=0.005)) | 
					
						
							|  |  |  |         self.assertTrue(sem.acquire(timeout=0.005)) | 
					
						
							|  |  |  |         self.assertFalse(sem.acquire(timeout=0.005)) | 
					
						
							|  |  |  |         sem.release() | 
					
						
							|  |  |  |         self.assertTrue(sem.acquire(timeout=0.005)) | 
					
						
							| 
									
										
										
										
											2018-12-17 09:36:36 +01:00
										 |  |  |         t = time.monotonic() | 
					
						
							| 
									
										
										
										
											2010-04-17 23:51:58 +00:00
										 |  |  |         self.assertFalse(sem.acquire(timeout=0.5)) | 
					
						
							| 
									
										
										
										
											2018-12-17 09:36:36 +01:00
										 |  |  |         dt = time.monotonic() - t | 
					
						
							| 
									
										
										
										
											2010-04-17 23:51:58 +00:00
										 |  |  |         self.assertTimeout(dt, 0.5) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  |     def test_default_value(self): | 
					
						
							|  |  |  |         # The default initial value is 1. | 
					
						
							|  |  |  |         sem = self.semtype() | 
					
						
							|  |  |  |         sem.acquire() | 
					
						
							|  |  |  |         def f(): | 
					
						
							|  |  |  |             sem.acquire() | 
					
						
							|  |  |  |             sem.release() | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  |         with Bunch(f, 1) as bunch: | 
					
						
							|  |  |  |             # Thread blocked on sem.acquire() | 
					
						
							|  |  |  |             wait_threads_blocked(1) | 
					
						
							|  |  |  |             self.assertFalse(bunch.finished) | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  |             # Thread unblocked | 
					
						
							|  |  |  |             sem.release() | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_with(self): | 
					
						
							|  |  |  |         sem = self.semtype(2) | 
					
						
							|  |  |  |         def _with(err=None): | 
					
						
							|  |  |  |             with sem: | 
					
						
							|  |  |  |                 self.assertTrue(sem.acquire(False)) | 
					
						
							|  |  |  |                 sem.release() | 
					
						
							|  |  |  |                 with sem: | 
					
						
							|  |  |  |                     self.assertFalse(sem.acquire(False)) | 
					
						
							|  |  |  |                     if err: | 
					
						
							|  |  |  |                         raise err | 
					
						
							|  |  |  |         _with() | 
					
						
							|  |  |  |         self.assertTrue(sem.acquire(False)) | 
					
						
							|  |  |  |         sem.release() | 
					
						
							|  |  |  |         self.assertRaises(TypeError, _with, TypeError) | 
					
						
							|  |  |  |         self.assertTrue(sem.acquire(False)) | 
					
						
							|  |  |  |         sem.release() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class SemaphoreTests(BaseSemaphoreTests): | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     Tests for unbounded semaphores. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_release_unacquired(self): | 
					
						
							|  |  |  |         # Unbounded releases are allowed and increment the semaphore's value | 
					
						
							|  |  |  |         sem = self.semtype(1) | 
					
						
							|  |  |  |         sem.release() | 
					
						
							|  |  |  |         sem.acquire() | 
					
						
							|  |  |  |         sem.acquire() | 
					
						
							|  |  |  |         sem.release() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-29 13:07:58 +03:00
										 |  |  |     def test_repr(self): | 
					
						
							|  |  |  |         sem = self.semtype(3) | 
					
						
							|  |  |  |         self.assertRegex(repr(sem), r"<\w+\.Semaphore at .*: value=3>") | 
					
						
							|  |  |  |         sem.acquire() | 
					
						
							|  |  |  |         self.assertRegex(repr(sem), r"<\w+\.Semaphore at .*: value=2>") | 
					
						
							|  |  |  |         sem.release() | 
					
						
							|  |  |  |         sem.release() | 
					
						
							|  |  |  |         self.assertRegex(repr(sem), r"<\w+\.Semaphore at .*: value=4>") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-11-06 22:41:14 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | class BoundedSemaphoreTests(BaseSemaphoreTests): | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     Tests for bounded semaphores. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_release_unacquired(self): | 
					
						
							|  |  |  |         # Cannot go past the initial value | 
					
						
							|  |  |  |         sem = self.semtype() | 
					
						
							|  |  |  |         self.assertRaises(ValueError, sem.release) | 
					
						
							|  |  |  |         sem.acquire() | 
					
						
							|  |  |  |         sem.release() | 
					
						
							|  |  |  |         self.assertRaises(ValueError, sem.release) | 
					
						
							| 
									
										
										
										
											2010-10-28 09:43:10 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-29 13:07:58 +03:00
										 |  |  |     def test_repr(self): | 
					
						
							|  |  |  |         sem = self.semtype(3) | 
					
						
							|  |  |  |         self.assertRegex(repr(sem), r"<\w+\.BoundedSemaphore at .*: value=3/3>") | 
					
						
							|  |  |  |         sem.acquire() | 
					
						
							|  |  |  |         self.assertRegex(repr(sem), r"<\w+\.BoundedSemaphore at .*: value=2/3>") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-28 09:43:10 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | class BarrierTests(BaseTestCase): | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     Tests for Barrier objects. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     N = 5 | 
					
						
							| 
									
										
										
										
											2010-11-17 21:55:41 +00:00
										 |  |  |     defaultTimeout = 2.0 | 
					
						
							| 
									
										
										
										
											2010-10-28 09:43:10 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def setUp(self): | 
					
						
							| 
									
										
										
										
											2010-10-31 03:00:57 +00:00
										 |  |  |         self.barrier = self.barriertype(self.N, timeout=self.defaultTimeout) | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-28 09:43:10 +00:00
										 |  |  |     def tearDown(self): | 
					
						
							|  |  |  |         self.barrier.abort() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def run_threads(self, f): | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  |         with Bunch(f, self.N): | 
					
						
							|  |  |  |             pass | 
					
						
							| 
									
										
										
										
											2010-10-28 09:43:10 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def multipass(self, results, n): | 
					
						
							|  |  |  |         m = self.barrier.parties | 
					
						
							|  |  |  |         self.assertEqual(m, self.N) | 
					
						
							|  |  |  |         for i in range(n): | 
					
						
							|  |  |  |             results[0].append(True) | 
					
						
							|  |  |  |             self.assertEqual(len(results[1]), i * m) | 
					
						
							|  |  |  |             self.barrier.wait() | 
					
						
							|  |  |  |             results[1].append(True) | 
					
						
							|  |  |  |             self.assertEqual(len(results[0]), (i + 1) * m) | 
					
						
							|  |  |  |             self.barrier.wait() | 
					
						
							|  |  |  |         self.assertEqual(self.barrier.n_waiting, 0) | 
					
						
							|  |  |  |         self.assertFalse(self.barrier.broken) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-30 04:53:07 -04:00
										 |  |  |     def test_constructor(self): | 
					
						
							|  |  |  |         self.assertRaises(ValueError, self.barriertype, parties=0) | 
					
						
							|  |  |  |         self.assertRaises(ValueError, self.barriertype, parties=-1) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-28 09:43:10 +00:00
										 |  |  |     def test_barrier(self, passes=1): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Test that a barrier is passed in lockstep | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         results = [[],[]] | 
					
						
							|  |  |  |         def f(): | 
					
						
							|  |  |  |             self.multipass(results, passes) | 
					
						
							|  |  |  |         self.run_threads(f) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_barrier_10(self): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Test that a barrier works for 10 consecutive runs | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         return self.test_barrier(10) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_wait_return(self): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         test the return value from barrier.wait | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         results = [] | 
					
						
							|  |  |  |         def f(): | 
					
						
							|  |  |  |             r = self.barrier.wait() | 
					
						
							|  |  |  |             results.append(r) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.run_threads(f) | 
					
						
							|  |  |  |         self.assertEqual(sum(results), sum(range(self.N))) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_action(self): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Test the 'action' callback | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         results = [] | 
					
						
							|  |  |  |         def action(): | 
					
						
							|  |  |  |             results.append(True) | 
					
						
							|  |  |  |         barrier = self.barriertype(self.N, action) | 
					
						
							|  |  |  |         def f(): | 
					
						
							|  |  |  |             barrier.wait() | 
					
						
							|  |  |  |             self.assertEqual(len(results), 1) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.run_threads(f) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_abort(self): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Test that an abort will put the barrier in a broken state | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         results1 = [] | 
					
						
							|  |  |  |         results2 = [] | 
					
						
							|  |  |  |         def f(): | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 i = self.barrier.wait() | 
					
						
							|  |  |  |                 if i == self.N//2: | 
					
						
							|  |  |  |                     raise RuntimeError | 
					
						
							|  |  |  |                 self.barrier.wait() | 
					
						
							|  |  |  |                 results1.append(True) | 
					
						
							|  |  |  |             except threading.BrokenBarrierError: | 
					
						
							|  |  |  |                 results2.append(True) | 
					
						
							|  |  |  |             except RuntimeError: | 
					
						
							|  |  |  |                 self.barrier.abort() | 
					
						
							|  |  |  |                 pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.run_threads(f) | 
					
						
							|  |  |  |         self.assertEqual(len(results1), 0) | 
					
						
							|  |  |  |         self.assertEqual(len(results2), self.N-1) | 
					
						
							|  |  |  |         self.assertTrue(self.barrier.broken) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_reset(self): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Test that a 'reset' on a barrier frees the waiting threads | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         results1 = [] | 
					
						
							|  |  |  |         results2 = [] | 
					
						
							|  |  |  |         results3 = [] | 
					
						
							|  |  |  |         def f(): | 
					
						
							|  |  |  |             i = self.barrier.wait() | 
					
						
							|  |  |  |             if i == self.N//2: | 
					
						
							|  |  |  |                 # Wait until the other threads are all in the barrier. | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  |                 for _ in support.sleeping_retry(support.SHORT_TIMEOUT): | 
					
						
							|  |  |  |                     if self.barrier.n_waiting >= (self.N - 1): | 
					
						
							|  |  |  |                         break | 
					
						
							| 
									
										
										
										
											2010-10-28 09:43:10 +00:00
										 |  |  |                 self.barrier.reset() | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 try: | 
					
						
							|  |  |  |                     self.barrier.wait() | 
					
						
							|  |  |  |                     results1.append(True) | 
					
						
							|  |  |  |                 except threading.BrokenBarrierError: | 
					
						
							|  |  |  |                     results2.append(True) | 
					
						
							|  |  |  |             # Now, pass the barrier again | 
					
						
							|  |  |  |             self.barrier.wait() | 
					
						
							|  |  |  |             results3.append(True) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.run_threads(f) | 
					
						
							|  |  |  |         self.assertEqual(len(results1), 0) | 
					
						
							|  |  |  |         self.assertEqual(len(results2), self.N-1) | 
					
						
							|  |  |  |         self.assertEqual(len(results3), self.N) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_abort_and_reset(self): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Test that a barrier can be reset after being broken. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         results1 = [] | 
					
						
							|  |  |  |         results2 = [] | 
					
						
							|  |  |  |         results3 = [] | 
					
						
							|  |  |  |         barrier2 = self.barriertype(self.N) | 
					
						
							|  |  |  |         def f(): | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 i = self.barrier.wait() | 
					
						
							|  |  |  |                 if i == self.N//2: | 
					
						
							|  |  |  |                     raise RuntimeError | 
					
						
							|  |  |  |                 self.barrier.wait() | 
					
						
							|  |  |  |                 results1.append(True) | 
					
						
							|  |  |  |             except threading.BrokenBarrierError: | 
					
						
							|  |  |  |                 results2.append(True) | 
					
						
							|  |  |  |             except RuntimeError: | 
					
						
							|  |  |  |                 self.barrier.abort() | 
					
						
							|  |  |  |                 pass | 
					
						
							|  |  |  |             # Synchronize and reset the barrier.  Must synchronize first so | 
					
						
							|  |  |  |             # that everyone has left it when we reset, and after so that no | 
					
						
							|  |  |  |             # one enters it before the reset. | 
					
						
							|  |  |  |             if barrier2.wait() == self.N//2: | 
					
						
							|  |  |  |                 self.barrier.reset() | 
					
						
							|  |  |  |             barrier2.wait() | 
					
						
							|  |  |  |             self.barrier.wait() | 
					
						
							|  |  |  |             results3.append(True) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.run_threads(f) | 
					
						
							|  |  |  |         self.assertEqual(len(results1), 0) | 
					
						
							|  |  |  |         self.assertEqual(len(results2), self.N-1) | 
					
						
							|  |  |  |         self.assertEqual(len(results3), self.N) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_timeout(self): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Test wait(timeout) | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         def f(): | 
					
						
							|  |  |  |             i = self.barrier.wait() | 
					
						
							|  |  |  |             if i == self.N // 2: | 
					
						
							|  |  |  |                 # One thread is late! | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  |                 time.sleep(self.defaultTimeout / 2) | 
					
						
							| 
									
										
										
										
											2010-11-17 21:55:41 +00:00
										 |  |  |             # Default timeout is 2.0, so this is shorter. | 
					
						
							| 
									
										
										
										
											2010-10-28 09:43:10 +00:00
										 |  |  |             self.assertRaises(threading.BrokenBarrierError, | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  |                               self.barrier.wait, self.defaultTimeout / 4) | 
					
						
							| 
									
										
										
										
											2010-10-28 09:43:10 +00:00
										 |  |  |         self.run_threads(f) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_default_timeout(self): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Test the barrier's default timeout | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  |         timeout = 0.100 | 
					
						
							|  |  |  |         barrier = self.barriertype(2, timeout=timeout) | 
					
						
							| 
									
										
										
										
											2010-10-28 09:43:10 +00:00
										 |  |  |         def f(): | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  |             self.assertRaises(threading.BrokenBarrierError, | 
					
						
							|  |  |  |                               barrier.wait) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         start_time = time.monotonic() | 
					
						
							|  |  |  |         with Bunch(f, 1): | 
					
						
							|  |  |  |             pass | 
					
						
							|  |  |  |         dt = time.monotonic() - start_time | 
					
						
							|  |  |  |         self.assertGreaterEqual(dt, timeout) | 
					
						
							| 
									
										
										
										
											2010-10-28 09:43:10 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_single_thread(self): | 
					
						
							|  |  |  |         b = self.barriertype(1) | 
					
						
							|  |  |  |         b.wait() | 
					
						
							|  |  |  |         b.wait() | 
					
						
							| 
									
										
										
										
											2021-09-29 13:07:58 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_repr(self): | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  |         barrier = self.barriertype(3) | 
					
						
							|  |  |  |         timeout = support.LONG_TIMEOUT | 
					
						
							|  |  |  |         self.assertRegex(repr(barrier), r"<\w+\.Barrier at .*: waiters=0/3>") | 
					
						
							| 
									
										
										
										
											2021-09-29 13:07:58 +03:00
										 |  |  |         def f(): | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  |             barrier.wait(timeout) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         N = 2 | 
					
						
							| 
									
										
										
										
											2023-09-29 14:21:18 +02:00
										 |  |  |         with Bunch(f, N): | 
					
						
							|  |  |  |             # Threads blocked on barrier.wait() | 
					
						
							|  |  |  |             for _ in support.sleeping_retry(support.SHORT_TIMEOUT): | 
					
						
							|  |  |  |                 if barrier.n_waiting >= N: | 
					
						
							|  |  |  |                     break | 
					
						
							|  |  |  |             self.assertRegex(repr(barrier), | 
					
						
							|  |  |  |                              r"<\w+\.Barrier at .*: waiters=2/3>") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # Threads unblocked | 
					
						
							|  |  |  |             barrier.wait(timeout) | 
					
						
							| 
									
										
										
										
											2023-09-29 02:34:27 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         self.assertRegex(repr(barrier), | 
					
						
							|  |  |  |                          r"<\w+\.Barrier at .*: waiters=0/3>") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Abort the barrier | 
					
						
							|  |  |  |         barrier.abort() | 
					
						
							|  |  |  |         self.assertRegex(repr(barrier), | 
					
						
							|  |  |  |                          r"<\w+\.Barrier at .*: broken>") |