mirror of
				https://github.com/python/cpython.git
				synced 2025-11-02 22:51:25 +00:00 
			
		
		
		
	* Use the 'p' format unit instead of manually called PyObject_IsTrue(). * Pass boolean value instead 0/1 integers to functions that needs boolean. * Convert some arguments to boolean only once.
		
			
				
	
	
		
			981 lines
		
	
	
	
		
			29 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			981 lines
		
	
	
	
		
			29 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
"""
 | 
						|
Various tests for synchronization primitives.
 | 
						|
"""
 | 
						|
 | 
						|
import sys
 | 
						|
import time
 | 
						|
from _thread import start_new_thread, TIMEOUT_MAX
 | 
						|
import threading
 | 
						|
import unittest
 | 
						|
import weakref
 | 
						|
 | 
						|
from test import support
 | 
						|
 | 
						|
 | 
						|
def _wait():
 | 
						|
    # A crude wait/yield function not relying on synchronization primitives.
 | 
						|
    time.sleep(0.01)
 | 
						|
 | 
						|
class Bunch(object):
 | 
						|
    """
 | 
						|
    A bunch of threads.
 | 
						|
    """
 | 
						|
    def __init__(self, f, n, wait_before_exit=False):
 | 
						|
        """
 | 
						|
        Construct a bunch of `n` threads running the same function `f`.
 | 
						|
        If `wait_before_exit` is True, the threads won't terminate until
 | 
						|
        do_finish() is called.
 | 
						|
        """
 | 
						|
        self.f = f
 | 
						|
        self.n = n
 | 
						|
        self.started = []
 | 
						|
        self.finished = []
 | 
						|
        self._can_exit = not wait_before_exit
 | 
						|
        self.wait_thread = support.wait_threads_exit()
 | 
						|
        self.wait_thread.__enter__()
 | 
						|
 | 
						|
        def task():
 | 
						|
            tid = threading.get_ident()
 | 
						|
            self.started.append(tid)
 | 
						|
            try:
 | 
						|
                f()
 | 
						|
            finally:
 | 
						|
                self.finished.append(tid)
 | 
						|
                while not self._can_exit:
 | 
						|
                    _wait()
 | 
						|
 | 
						|
        try:
 | 
						|
            for i in range(n):
 | 
						|
                start_new_thread(task, ())
 | 
						|
        except:
 | 
						|
            self._can_exit = True
 | 
						|
            raise
 | 
						|
 | 
						|
    def wait_for_started(self):
 | 
						|
        while len(self.started) < self.n:
 | 
						|
            _wait()
 | 
						|
 | 
						|
    def wait_for_finished(self):
 | 
						|
        while len(self.finished) < self.n:
 | 
						|
            _wait()
 | 
						|
        # Wait for threads exit
 | 
						|
        self.wait_thread.__exit__(None, None, None)
 | 
						|
 | 
						|
    def do_finish(self):
 | 
						|
        self._can_exit = True
 | 
						|
 | 
						|
 | 
						|
class BaseTestCase(unittest.TestCase):
 | 
						|
    def setUp(self):
 | 
						|
        self._threads = support.threading_setup()
 | 
						|
 | 
						|
    def tearDown(self):
 | 
						|
        support.threading_cleanup(*self._threads)
 | 
						|
        support.reap_children()
 | 
						|
 | 
						|
    def assertTimeout(self, actual, expected):
 | 
						|
        # The waiting and/or time.monotonic() can be imprecise, which
 | 
						|
        # 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)
 | 
						|
 | 
						|
 | 
						|
class BaseLockTests(BaseTestCase):
 | 
						|
    """
 | 
						|
    Tests for both recursive and non-recursive locks.
 | 
						|
    """
 | 
						|
 | 
						|
    def test_constructor(self):
 | 
						|
        lock = self.locktype()
 | 
						|
        del lock
 | 
						|
 | 
						|
    def test_repr(self):
 | 
						|
        lock = self.locktype()
 | 
						|
        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 .*>")
 | 
						|
        del lock
 | 
						|
 | 
						|
    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))
 | 
						|
        Bunch(f, 1).wait_for_finished()
 | 
						|
        self.assertFalse(result[0])
 | 
						|
        lock.release()
 | 
						|
 | 
						|
    def test_acquire_contended(self):
 | 
						|
        lock = self.locktype()
 | 
						|
        lock.acquire()
 | 
						|
        N = 5
 | 
						|
        def f():
 | 
						|
            lock.acquire()
 | 
						|
            lock.release()
 | 
						|
 | 
						|
        b = Bunch(f, N)
 | 
						|
        b.wait_for_started()
 | 
						|
        _wait()
 | 
						|
        self.assertEqual(len(b.finished), 0)
 | 
						|
        lock.release()
 | 
						|
        b.wait_for_finished()
 | 
						|
        self.assertEqual(len(b.finished), N)
 | 
						|
 | 
						|
    def test_with(self):
 | 
						|
        lock = self.locktype()
 | 
						|
        def f():
 | 
						|
            lock.acquire()
 | 
						|
            lock.release()
 | 
						|
        def _with(err=None):
 | 
						|
            with lock:
 | 
						|
                if err is not None:
 | 
						|
                    raise err
 | 
						|
        _with()
 | 
						|
        # Check the lock is unacquired
 | 
						|
        Bunch(f, 1).wait_for_finished()
 | 
						|
        self.assertRaises(TypeError, _with, TypeError)
 | 
						|
        # Check the lock is unacquired
 | 
						|
        Bunch(f, 1).wait_for_finished()
 | 
						|
 | 
						|
    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()
 | 
						|
        n = len(threading.enumerate())
 | 
						|
        # We run many threads in the hope that existing threads ids won't
 | 
						|
        # be recycled.
 | 
						|
        Bunch(f, 15).wait_for_finished()
 | 
						|
        if len(threading.enumerate()) != n:
 | 
						|
            # There is a small window during which a Thread instance's
 | 
						|
            # target function has finished running, but the Thread is still
 | 
						|
            # alive and registered.  Avoid spurious failures by waiting a
 | 
						|
            # bit more (seen on a buildbot).
 | 
						|
            time.sleep(0.4)
 | 
						|
            self.assertEqual(n, len(threading.enumerate()))
 | 
						|
 | 
						|
    def test_timeout(self):
 | 
						|
        lock = self.locktype()
 | 
						|
        # Can't set timeout if not blocking
 | 
						|
        self.assertRaises(ValueError, lock.acquire, False, 1)
 | 
						|
        # 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()
 | 
						|
        t1 = time.monotonic()
 | 
						|
        self.assertTrue(lock.acquire(timeout=5))
 | 
						|
        t2 = time.monotonic()
 | 
						|
        # Just a sanity test that it didn't actually wait for the timeout.
 | 
						|
        self.assertLess(t2 - t1, 5)
 | 
						|
        results = []
 | 
						|
        def f():
 | 
						|
            t1 = time.monotonic()
 | 
						|
            results.append(lock.acquire(timeout=0.5))
 | 
						|
            t2 = time.monotonic()
 | 
						|
            results.append(t2 - t1)
 | 
						|
        Bunch(f, 1).wait_for_finished()
 | 
						|
        self.assertFalse(results[0])
 | 
						|
        self.assertTimeout(results[1], 0.5)
 | 
						|
 | 
						|
    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
 | 
						|
        self.assertIsNone(ref())
 | 
						|
 | 
						|
 | 
						|
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 = []
 | 
						|
 | 
						|
        def f():
 | 
						|
            lock.acquire()
 | 
						|
            phase.append(None)
 | 
						|
            lock.acquire()
 | 
						|
            phase.append(None)
 | 
						|
 | 
						|
        with support.wait_threads_exit():
 | 
						|
            start_new_thread(f, ())
 | 
						|
            while len(phase) == 0:
 | 
						|
                _wait()
 | 
						|
            _wait()
 | 
						|
            self.assertEqual(len(phase), 1)
 | 
						|
            lock.release()
 | 
						|
            while len(phase) == 1:
 | 
						|
                _wait()
 | 
						|
            self.assertEqual(len(phase), 2)
 | 
						|
 | 
						|
    def test_different_thread(self):
 | 
						|
        # Lock can be released from a different thread.
 | 
						|
        lock = self.locktype()
 | 
						|
        lock.acquire()
 | 
						|
        def f():
 | 
						|
            lock.release()
 | 
						|
        b = Bunch(f, 1)
 | 
						|
        b.wait_for_finished()
 | 
						|
        lock.acquire()
 | 
						|
        lock.release()
 | 
						|
 | 
						|
    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))
 | 
						|
 | 
						|
 | 
						|
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)
 | 
						|
 | 
						|
    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()
 | 
						|
        self.assertRaises(RuntimeError, lock._release_save)
 | 
						|
 | 
						|
    def test_different_thread(self):
 | 
						|
        # Cannot release from a different thread
 | 
						|
        lock = self.locktype()
 | 
						|
        def f():
 | 
						|
            lock.acquire()
 | 
						|
        b = Bunch(f, 1, True)
 | 
						|
        try:
 | 
						|
            self.assertRaises(RuntimeError, lock.release)
 | 
						|
        finally:
 | 
						|
            b.do_finish()
 | 
						|
        b.wait_for_finished()
 | 
						|
 | 
						|
    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())
 | 
						|
        Bunch(f, 1).wait_for_finished()
 | 
						|
        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())
 | 
						|
        b = Bunch(f, N)
 | 
						|
        b.wait_for_started()
 | 
						|
        _wait()
 | 
						|
        self.assertEqual(len(results1), 0)
 | 
						|
        evt.set()
 | 
						|
        b.wait_for_finished()
 | 
						|
        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))
 | 
						|
            t1 = time.monotonic()
 | 
						|
            r = evt.wait(0.5)
 | 
						|
            t2 = time.monotonic()
 | 
						|
            results2.append((r, t2 - t1))
 | 
						|
        Bunch(f, N).wait_for_finished()
 | 
						|
        self.assertEqual(results1, [False] * N)
 | 
						|
        for r, dt in results2:
 | 
						|
            self.assertFalse(r)
 | 
						|
            self.assertTimeout(dt, 0.5)
 | 
						|
        # The event is set
 | 
						|
        results1 = []
 | 
						|
        results2 = []
 | 
						|
        evt.set()
 | 
						|
        Bunch(f, N).wait_for_finished()
 | 
						|
        self.assertEqual(results1, [True] * N)
 | 
						|
        for r, dt in results2:
 | 
						|
            self.assertTrue(r)
 | 
						|
 | 
						|
    def test_set_and_clear(self):
 | 
						|
        # Issue #13502: check that wait() returns true even when the event is
 | 
						|
        # cleared before the waiting thread is woken up.
 | 
						|
        evt = self.eventtype()
 | 
						|
        results = []
 | 
						|
        timeout = 0.250
 | 
						|
        N = 5
 | 
						|
        def f():
 | 
						|
            results.append(evt.wait(timeout * 4))
 | 
						|
        b = Bunch(f, N)
 | 
						|
        b.wait_for_started()
 | 
						|
        time.sleep(timeout)
 | 
						|
        evt.set()
 | 
						|
        evt.clear()
 | 
						|
        b.wait_for_finished()
 | 
						|
        self.assertEqual(results, [True] * N)
 | 
						|
 | 
						|
    def test_reset_internal_locks(self):
 | 
						|
        # ensure that condition is still using a Lock after reset
 | 
						|
        evt = self.eventtype()
 | 
						|
        with evt._cond:
 | 
						|
            self.assertFalse(evt._cond.acquire(False))
 | 
						|
        evt._reset_internal_locks()
 | 
						|
        with evt._cond:
 | 
						|
            self.assertFalse(evt._cond.acquire(False))
 | 
						|
 | 
						|
 | 
						|
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):
 | 
						|
        # 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
 | 
						|
        # _wait() statements to try to make sure that it doesn't race ahead
 | 
						|
        # of the workers.
 | 
						|
        # Secondly, this test assumes that condition variables are not subject
 | 
						|
        # to spurious wakeups.  The absence of spurious wakeups is an implementation
 | 
						|
        # detail of Condition Variables in current CPython, but in general, not
 | 
						|
        # 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.
 | 
						|
        N = 5
 | 
						|
        ready = []
 | 
						|
        results1 = []
 | 
						|
        results2 = []
 | 
						|
        phase_num = 0
 | 
						|
        def f():
 | 
						|
            cond.acquire()
 | 
						|
            ready.append(phase_num)
 | 
						|
            result = cond.wait()
 | 
						|
            cond.release()
 | 
						|
            results1.append((result, phase_num))
 | 
						|
            cond.acquire()
 | 
						|
            ready.append(phase_num)
 | 
						|
            result = cond.wait()
 | 
						|
            cond.release()
 | 
						|
            results2.append((result, phase_num))
 | 
						|
        b = Bunch(f, N)
 | 
						|
        b.wait_for_started()
 | 
						|
        # first wait, to ensure all workers settle into cond.wait() before
 | 
						|
        # we continue. See issues #8799 and #30727.
 | 
						|
        while len(ready) < 5:
 | 
						|
            _wait()
 | 
						|
        ready.clear()
 | 
						|
        self.assertEqual(results1, [])
 | 
						|
        # Notify 3 threads at first
 | 
						|
        cond.acquire()
 | 
						|
        cond.notify(3)
 | 
						|
        _wait()
 | 
						|
        phase_num = 1
 | 
						|
        cond.release()
 | 
						|
        while len(results1) < 3:
 | 
						|
            _wait()
 | 
						|
        self.assertEqual(results1, [(True, 1)] * 3)
 | 
						|
        self.assertEqual(results2, [])
 | 
						|
        # make sure all awaken workers settle into cond.wait()
 | 
						|
        while len(ready) < 3:
 | 
						|
            _wait()
 | 
						|
        # Notify 5 threads: they might be in their first or second wait
 | 
						|
        cond.acquire()
 | 
						|
        cond.notify(5)
 | 
						|
        _wait()
 | 
						|
        phase_num = 2
 | 
						|
        cond.release()
 | 
						|
        while len(results1) + len(results2) < 8:
 | 
						|
            _wait()
 | 
						|
        self.assertEqual(results1, [(True, 1)] * 3 + [(True, 2)] * 2)
 | 
						|
        self.assertEqual(results2, [(True, 2)] * 3)
 | 
						|
        # make sure all workers settle into cond.wait()
 | 
						|
        while len(ready) < 5:
 | 
						|
            _wait()
 | 
						|
        # Notify all threads: they are all in their second wait
 | 
						|
        cond.acquire()
 | 
						|
        cond.notify_all()
 | 
						|
        _wait()
 | 
						|
        phase_num = 3
 | 
						|
        cond.release()
 | 
						|
        while len(results2) < 5:
 | 
						|
            _wait()
 | 
						|
        self.assertEqual(results1, [(True, 1)] * 3 + [(True,2)] * 2)
 | 
						|
        self.assertEqual(results2, [(True, 2)] * 3 + [(True, 3)] * 2)
 | 
						|
        b.wait_for_finished()
 | 
						|
 | 
						|
    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()
 | 
						|
        results = []
 | 
						|
        N = 5
 | 
						|
        def f():
 | 
						|
            cond.acquire()
 | 
						|
            t1 = time.monotonic()
 | 
						|
            result = cond.wait(0.5)
 | 
						|
            t2 = time.monotonic()
 | 
						|
            cond.release()
 | 
						|
            results.append((t2 - t1, result))
 | 
						|
        Bunch(f, N).wait_for_finished()
 | 
						|
        self.assertEqual(len(results), N)
 | 
						|
        for dt, result in results:
 | 
						|
            self.assertTimeout(dt, 0.5)
 | 
						|
            # 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)
 | 
						|
 | 
						|
    def test_waitfor(self):
 | 
						|
        cond = self.condtype()
 | 
						|
        state = 0
 | 
						|
        def f():
 | 
						|
            with cond:
 | 
						|
                result = cond.wait_for(lambda : state==4)
 | 
						|
                self.assertTrue(result)
 | 
						|
                self.assertEqual(state, 4)
 | 
						|
        b = Bunch(f, 1)
 | 
						|
        b.wait_for_started()
 | 
						|
        for i in range(4):
 | 
						|
            time.sleep(0.01)
 | 
						|
            with cond:
 | 
						|
                state += 1
 | 
						|
                cond.notify()
 | 
						|
        b.wait_for_finished()
 | 
						|
 | 
						|
    def test_waitfor_timeout(self):
 | 
						|
        cond = self.condtype()
 | 
						|
        state = 0
 | 
						|
        success = []
 | 
						|
        def f():
 | 
						|
            with cond:
 | 
						|
                dt = time.monotonic()
 | 
						|
                result = cond.wait_for(lambda : state==4, timeout=0.1)
 | 
						|
                dt = time.monotonic() - dt
 | 
						|
                self.assertFalse(result)
 | 
						|
                self.assertTimeout(dt, 0.1)
 | 
						|
                success.append(None)
 | 
						|
        b = Bunch(f, 1)
 | 
						|
        b.wait_for_started()
 | 
						|
        # Only increment 3 times, so state == 4 is never reached.
 | 
						|
        for i in range(3):
 | 
						|
            time.sleep(0.01)
 | 
						|
            with cond:
 | 
						|
                state += 1
 | 
						|
                cond.notify()
 | 
						|
        b.wait_for_finished()
 | 
						|
        self.assertEqual(len(success), 1)
 | 
						|
 | 
						|
 | 
						|
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):
 | 
						|
        sem = self.semtype(7)
 | 
						|
        sem.acquire()
 | 
						|
        N = 10
 | 
						|
        sem_results = []
 | 
						|
        results1 = []
 | 
						|
        results2 = []
 | 
						|
        phase_num = 0
 | 
						|
        def f():
 | 
						|
            sem_results.append(sem.acquire())
 | 
						|
            results1.append(phase_num)
 | 
						|
            sem_results.append(sem.acquire())
 | 
						|
            results2.append(phase_num)
 | 
						|
        b = Bunch(f, 10)
 | 
						|
        b.wait_for_started()
 | 
						|
        while len(results1) + len(results2) < 6:
 | 
						|
            _wait()
 | 
						|
        self.assertEqual(results1 + results2, [0] * 6)
 | 
						|
        phase_num = 1
 | 
						|
        for i in range(7):
 | 
						|
            sem.release()
 | 
						|
        while len(results1) + len(results2) < 13:
 | 
						|
            _wait()
 | 
						|
        self.assertEqual(sorted(results1 + results2), [0] * 6 + [1] * 7)
 | 
						|
        phase_num = 2
 | 
						|
        for i in range(6):
 | 
						|
            sem.release()
 | 
						|
        while len(results1) + len(results2) < 19:
 | 
						|
            _wait()
 | 
						|
        self.assertEqual(sorted(results1 + results2), [0] * 6 + [1] * 7 + [2] * 6)
 | 
						|
        # The semaphore is still locked
 | 
						|
        self.assertFalse(sem.acquire(False))
 | 
						|
        # Final release, to let the last thread finish
 | 
						|
        sem.release()
 | 
						|
        b.wait_for_finished()
 | 
						|
        self.assertEqual(sem_results, [True] * (6 + 7 + 6 + 1))
 | 
						|
 | 
						|
    def test_multirelease(self):
 | 
						|
        sem = self.semtype(7)
 | 
						|
        sem.acquire()
 | 
						|
        results1 = []
 | 
						|
        results2 = []
 | 
						|
        phase_num = 0
 | 
						|
        def f():
 | 
						|
            sem.acquire()
 | 
						|
            results1.append(phase_num)
 | 
						|
            sem.acquire()
 | 
						|
            results2.append(phase_num)
 | 
						|
        b = Bunch(f, 10)
 | 
						|
        b.wait_for_started()
 | 
						|
        while len(results1) + len(results2) < 6:
 | 
						|
            _wait()
 | 
						|
        self.assertEqual(results1 + results2, [0] * 6)
 | 
						|
        phase_num = 1
 | 
						|
        sem.release(7)
 | 
						|
        while len(results1) + len(results2) < 13:
 | 
						|
            _wait()
 | 
						|
        self.assertEqual(sorted(results1 + results2), [0] * 6 + [1] * 7)
 | 
						|
        phase_num = 2
 | 
						|
        sem.release(6)
 | 
						|
        while len(results1) + len(results2) < 19:
 | 
						|
            _wait()
 | 
						|
        self.assertEqual(sorted(results1 + results2), [0] * 6 + [1] * 7 + [2] * 6)
 | 
						|
        # The semaphore is still locked
 | 
						|
        self.assertFalse(sem.acquire(False))
 | 
						|
        # Final release, to let the last thread finish
 | 
						|
        sem.release()
 | 
						|
        b.wait_for_finished()
 | 
						|
 | 
						|
    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))
 | 
						|
        Bunch(f, 5).wait_for_finished()
 | 
						|
        # 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 )
 | 
						|
 | 
						|
    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))
 | 
						|
        t = time.monotonic()
 | 
						|
        self.assertFalse(sem.acquire(timeout=0.5))
 | 
						|
        dt = time.monotonic() - t
 | 
						|
        self.assertTimeout(dt, 0.5)
 | 
						|
 | 
						|
    def test_default_value(self):
 | 
						|
        # The default initial value is 1.
 | 
						|
        sem = self.semtype()
 | 
						|
        sem.acquire()
 | 
						|
        def f():
 | 
						|
            sem.acquire()
 | 
						|
            sem.release()
 | 
						|
        b = Bunch(f, 1)
 | 
						|
        b.wait_for_started()
 | 
						|
        _wait()
 | 
						|
        self.assertFalse(b.finished)
 | 
						|
        sem.release()
 | 
						|
        b.wait_for_finished()
 | 
						|
 | 
						|
    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()
 | 
						|
 | 
						|
 | 
						|
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)
 | 
						|
 | 
						|
 | 
						|
class BarrierTests(BaseTestCase):
 | 
						|
    """
 | 
						|
    Tests for Barrier objects.
 | 
						|
    """
 | 
						|
    N = 5
 | 
						|
    defaultTimeout = 2.0
 | 
						|
 | 
						|
    def setUp(self):
 | 
						|
        self.barrier = self.barriertype(self.N, timeout=self.defaultTimeout)
 | 
						|
    def tearDown(self):
 | 
						|
        self.barrier.abort()
 | 
						|
 | 
						|
    def run_threads(self, f):
 | 
						|
        b = Bunch(f, self.N-1)
 | 
						|
        f()
 | 
						|
        b.wait_for_finished()
 | 
						|
 | 
						|
    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)
 | 
						|
 | 
						|
    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.
 | 
						|
                while self.barrier.n_waiting < self.N-1:
 | 
						|
                    time.sleep(0.001)
 | 
						|
                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!
 | 
						|
                time.sleep(1.0)
 | 
						|
            # Default timeout is 2.0, so this is shorter.
 | 
						|
            self.assertRaises(threading.BrokenBarrierError,
 | 
						|
                              self.barrier.wait, 0.5)
 | 
						|
        self.run_threads(f)
 | 
						|
 | 
						|
    def test_default_timeout(self):
 | 
						|
        """
 | 
						|
        Test the barrier's default timeout
 | 
						|
        """
 | 
						|
        # create a barrier with a low default timeout
 | 
						|
        barrier = self.barriertype(self.N, timeout=0.3)
 | 
						|
        def f():
 | 
						|
            i = barrier.wait()
 | 
						|
            if i == self.N // 2:
 | 
						|
                # One thread is later than the default timeout of 0.3s.
 | 
						|
                time.sleep(1.0)
 | 
						|
            self.assertRaises(threading.BrokenBarrierError, barrier.wait)
 | 
						|
        self.run_threads(f)
 | 
						|
 | 
						|
    def test_single_thread(self):
 | 
						|
        b = self.barriertype(1)
 | 
						|
        b.wait()
 | 
						|
        b.wait()
 |