Commit 0454af9b authored by Antoine Pitrou's avatar Antoine Pitrou

Issue #850728: Add a *timeout* parameter to the `acquire()` method of

`threading.Semaphore` objects.  Original patch by Torsten Landschoff.
parent 2d9cb9c1
...@@ -596,7 +596,7 @@ waiting until some other thread calls :meth:`release`. ...@@ -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 defaults to ``1``. If the *value* given is less than 0, :exc:`ValueError` is
raised. raised.
.. method:: acquire(blocking=True) .. method:: acquire(blocking=True, timeout=None)
Acquire a semaphore. Acquire a semaphore.
...@@ -607,14 +607,18 @@ waiting until some other thread calls :meth:`release`. ...@@ -607,14 +607,18 @@ waiting until some other thread calls :meth:`release`.
interlocking so that if multiple :meth:`acquire` calls are blocked, interlocking so that if multiple :meth:`acquire` calls are blocked,
:meth:`release` will wake exactly one of them up. The implementation may :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 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. should not be relied on. Returns true (or blocks indefinitely).
When invoked with *blocking* set to true, do the same thing as when called
without arguments, and return true.
When invoked with *blocking* set to false, do not block. If a call When invoked with *blocking* set to false, do not block. If a call
without an argument would block, return false immediately; otherwise, do without an argument would block, return false immediately; otherwise,
the same thing as when called without arguments, and return true. 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() .. method:: release()
......
...@@ -521,6 +521,19 @@ class BaseSemaphoreTests(BaseTestCase): ...@@ -521,6 +521,19 @@ class BaseSemaphoreTests(BaseTestCase):
# ordered. # ordered.
self.assertEqual(sorted(results), [False] * 7 + [True] * 3 ) 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): def test_default_value(self):
# The default initial value is 1. # The default initial value is 1.
sem = self.semtype() sem = self.semtype()
......
...@@ -290,8 +290,11 @@ class _Semaphore(_Verbose): ...@@ -290,8 +290,11 @@ class _Semaphore(_Verbose):
self._cond = Condition(Lock()) self._cond = Condition(Lock())
self._value = value 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 rc = False
endtime = None
self._cond.acquire() self._cond.acquire()
while self._value == 0: while self._value == 0:
if not blocking: if not blocking:
...@@ -299,7 +302,14 @@ class _Semaphore(_Verbose): ...@@ -299,7 +302,14 @@ class _Semaphore(_Verbose):
if __debug__: if __debug__:
self._note("%s.acquire(%s): blocked waiting, value=%s", self._note("%s.acquire(%s): blocked waiting, value=%s",
self, blocking, self._value) 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: else:
self._value = self._value - 1 self._value = self._value - 1
if __debug__: if __debug__:
......
...@@ -431,6 +431,7 @@ Ivan Krstić ...@@ -431,6 +431,7 @@ Ivan Krstić
Andrew Kuchling Andrew Kuchling
Vladimir Kushnir Vladimir Kushnir
Cameron Laird Cameron Laird
Torsten Landschoff
Tino Lange Tino Lange
Andrew Langmead Andrew Langmead
Detlef Lannert Detlef Lannert
......
...@@ -315,6 +315,9 @@ C-API ...@@ -315,6 +315,9 @@ C-API
Library 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 - 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. available cipher list. Helps fix test_ssl with OpenSSL 1.0.0.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment