From a0374dd34aa25f0895195d388b5ceff43b121b00 Mon Sep 17 00:00:00 2001 From: Garrett Berg Date: Thu, 7 Dec 2017 11:04:26 -0700 Subject: [PATCH] bpo-32208: update threading.Semaphore docs and add unit test (#4709) * fix issue32208: update threading.Semaphore docs and add unit test to validate correct behavior * add test for blocking * Update threading.rst * semaphore: remove documentation validation tests and move 'return value' test to BaseSemaphore --- Doc/library/threading.rst | 26 +++++++++++++------------- Lib/test/lock_tests.py | 6 ++++-- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/Doc/library/threading.rst b/Doc/library/threading.rst index 95e27cab7c8..b94021b4eb8 100644 --- a/Doc/library/threading.rst +++ b/Doc/library/threading.rst @@ -684,8 +684,8 @@ Semaphores also support the :ref:`context management protocol `. .. class:: Semaphore(value=1) - This class implements semaphore objects. A semaphore manages a counter - representing the number of :meth:`release` calls minus the number of + This class implements semaphore objects. A semaphore manages an atomic + counter representing the number of :meth:`release` calls minus the number of :meth:`acquire` calls, plus an initial value. The :meth:`acquire` method blocks if necessary until it can return without making the counter negative. If not given, *value* defaults to 1. @@ -701,19 +701,19 @@ Semaphores also support the :ref:`context management protocol `. Acquire a semaphore. - When invoked without arguments: if the internal counter is larger than - zero on entry, decrement it by one and return immediately. If it is zero - on entry, block, waiting until some other thread has called - :meth:`~Semaphore.release` to make it larger than zero. This is done - with proper interlocking so that if multiple :meth:`acquire` calls are - blocked, :meth:`~Semaphore.release` will wake exactly one of them up. - The implementation may pick one at random, so the order in which - blocked threads are awakened should not be relied on. Returns - true (or blocks indefinitely). + When invoked without arguments: + + * If the internal counter is larger than zero on entry, decrement it by + one and return true immediately. + * If the internal counter is zero on entry, block until awoken by a call to + :meth:`~Semaphore.release`. Once awoken (and the counter is greater + than 0), decrement the counter by 1 and return true. Exactly one + thread will be awoken by each call to :meth:`~Semaphore.release`. The + order in which threads are awoken should not be relied on. When invoked with *blocking* set to false, do not block. If a call - without an argument would block, return false immediately; otherwise, - do the same thing as when called without arguments, and return true. + without an argument would block, return false immediately; otherwise, do + the same thing as when called without arguments, and return true. When invoked with a *timeout* other than ``None``, it will block for at most *timeout* seconds. If acquire does not complete successfully in diff --git a/Lib/test/lock_tests.py b/Lib/test/lock_tests.py index a1ea96d42ce..5b1f033c6f8 100644 --- a/Lib/test/lock_tests.py +++ b/Lib/test/lock_tests.py @@ -629,13 +629,14 @@ class BaseSemaphoreTests(BaseTestCase): sem = self.semtype(7) sem.acquire() N = 10 + sem_results = [] results1 = [] results2 = [] phase_num = 0 def f(): - sem.acquire() + sem_results.append(sem.acquire()) results1.append(phase_num) - sem.acquire() + sem_results.append(sem.acquire()) results2.append(phase_num) b = Bunch(f, 10) b.wait_for_started() @@ -659,6 +660,7 @@ class BaseSemaphoreTests(BaseTestCase): # 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_try_acquire(self): sem = self.semtype(2)