summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/library/threading.rst18
-rw-r--r--Lib/test/lock_tests.py13
-rw-r--r--Lib/threading.py14
-rw-r--r--Misc/ACKS1
-rw-r--r--Misc/NEWS3
5 files changed, 40 insertions, 9 deletions
diff --git a/Doc/library/threading.rst b/Doc/library/threading.rst
index 1f2b763..0ce6c63 100644
--- a/Doc/library/threading.rst
+++ b/Doc/library/threading.rst
@@ -596,7 +596,7 @@ waiting until some other thread calls :meth:`release`.
defaults to ``1``. If the *value* given is less than 0, :exc:`ValueError` is
raised.
- .. method:: acquire(blocking=True)
+ .. method:: acquire(blocking=True, timeout=None)
Acquire a semaphore.
@@ -607,14 +607,18 @@ waiting until some other thread calls :meth:`release`.
interlocking so that if multiple :meth:`acquire` calls are blocked,
:meth:`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. There is no return value in this case.
-
- When invoked with *blocking* set to true, do the same thing as when called
- without arguments, and return true.
+ should not be relied on. Returns true (or blocks indefinitely).
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
+ that interval, return false. Return true otherwise.
+
+ .. versionchanged:: 3.2
+ The *timeout* parameter is new.
.. method:: release()
diff --git a/Lib/test/lock_tests.py b/Lib/test/lock_tests.py
index 74db3e4..a288176 100644
--- a/Lib/test/lock_tests.py
+++ b/Lib/test/lock_tests.py
@@ -521,6 +521,19 @@ class BaseSemaphoreTests(BaseTestCase):
# 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.time()
+ self.assertFalse(sem.acquire(timeout=0.5))
+ dt = time.time() - t
+ self.assertTimeout(dt, 0.5)
+
def test_default_value(self):
# The default initial value is 1.
sem = self.semtype()
diff --git a/Lib/threading.py b/Lib/threading.py
index b4d07fe..2ca224e 100644
--- a/Lib/threading.py
+++ b/Lib/threading.py
@@ -290,8 +290,11 @@ class _Semaphore(_Verbose):
self._cond = Condition(Lock())
self._value = value
- def acquire(self, blocking=True):
+ def acquire(self, blocking=True, timeout=None):
+ if not blocking and timeout is not None:
+ raise ValueError("can't specify timeout for non-blocking acquire")
rc = False
+ endtime = None
self._cond.acquire()
while self._value == 0:
if not blocking:
@@ -299,7 +302,14 @@ class _Semaphore(_Verbose):
if __debug__:
self._note("%s.acquire(%s): blocked waiting, value=%s",
self, blocking, self._value)
- self._cond.wait()
+ if timeout is not None:
+ if endtime is None:
+ endtime = _time() + timeout
+ else:
+ timeout = endtime - _time()
+ if timeout <= 0:
+ break
+ self._cond.wait(timeout)
else:
self._value = self._value - 1
if __debug__:
diff --git a/Misc/ACKS b/Misc/ACKS
index 4471ab1..276a7af 100644
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -431,6 +431,7 @@ Ivan Krstić
Andrew Kuchling
Vladimir Kushnir
Cameron Laird
+Torsten Landschoff
Tino Lange
Andrew Langmead
Detlef Lannert
diff --git a/Misc/NEWS b/Misc/NEWS
index 203fd07..c758984 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -315,6 +315,9 @@ C-API
Library
-------
+- Issue #850728: Add a *timeout* parameter to the `acquire()` method of
+ `threading.Semaphore` objects. Patch by Torsten Landschoff.
+
- Issue #8322: Add a *ciphers* argument to SSL sockets, so as to change the
available cipher list. Helps fix test_ssl with OpenSSL 1.0.0.