# HG changeset patch # User Torsten Landschoff # Date 1266624023 -3600 # Branch trunk # Node ID a7f57dfde38c1bb52057a215084e7e4d65a87183 # Parent f32cf16abae7a3ebf62a270eece4a354ade948e1 New optional timeout parameter for Semaphore.acquire(). Updated patch based on the patch by Dmitry Vasiliev in from issue 850728, including test cases and documentation. diff -r f32cf16abae7 -r a7f57dfde38c Doc/library/threading.rst --- a/Doc/library/threading.rst Fri Feb 19 20:36:08 2010 +0100 +++ b/Doc/library/threading.rst Sat Feb 20 01:00:23 2010 +0100 @@ -591,7 +591,7 @@ defaults to ``1``. If the *value* given is less than 0, :exc:`ValueError` is raised. - .. method:: acquire([blocking]) + .. method:: acquire([blocking], [timeout]) Acquire a semaphore. @@ -602,14 +602,20 @@ 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. + should not be relied on. Returns true (or blocks indefinitely). - When invoked with *blocking* set to true, do the same thing as when called + If a *timeout* other than None is passed and *blocking* is true (or not + given at all), it will block for at most *timeout* seconds. If acquire + does not complete successfully in that interval, return false. Return + true otherwise. + + When invoked with *blocking* set to false, do not block (equivalent to + passing a zero *timeout*). If a call without an argument would block, + return false immediately; otherwise, 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 - without an argument would block, return false immediately; otherwise, do - the same thing as when called without arguments, and return true. + .. versionchanged:: 2.7 + The *timeout* parameter is new. .. method:: release() diff -r f32cf16abae7 -r a7f57dfde38c Lib/test/lock_tests.py --- a/Lib/test/lock_tests.py Fri Feb 19 20:36:08 2010 +0100 +++ b/Lib/test/lock_tests.py Sat Feb 20 01:00:23 2010 +0100 @@ -487,6 +487,30 @@ # ordered. self.assertEqual(sorted(results), [False] * 7 + [True] * 3 ) + def test_acquire_timeout(self): + sem = self.semtype(2) + 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)) + + timeout = 1.0 + fuzz = 2.0 + t0 = time.time() + self.assertFalse(sem.acquire(timeout=timeout)) + delta = time.time() - t0 + self.assertTrue(delta >= timeout and delta < timeout+fuzz) + + def test_acquire_timeout_nonblock(self): + sem = self.semtype(0) + timeout = 1.0 + fuzz = 0.1 + t0 = time.time() + self.assertFalse(sem.acquire(blocking=False, timeout=timeout)) + dt = time.time() - t0 + self.assertTrue(dt <= fuzz * timeout) + def test_default_value(self): # The default initial value is 1. sem = self.semtype() diff -r f32cf16abae7 -r a7f57dfde38c Lib/threading.py --- a/Lib/threading.py Fri Feb 19 20:36:08 2010 +0100 +++ b/Lib/threading.py Sat Feb 20 01:00:23 2010 +0100 @@ -306,7 +306,7 @@ self.__cond = Condition(Lock()) self.__value = value - def acquire(self, blocking=1): + def acquire(self, blocking=True, timeout=None): rc = False self.__cond.acquire() while self.__value == 0: @@ -315,7 +315,13 @@ if __debug__: self._note("%s.acquire(%s): blocked waiting, value=%s", self, blocking, self.__value) - self.__cond.wait() + if timeout is not None: + endtime = _time() + timeout + self.__cond.wait(timeout) + if timeout is not None and self.__value == 0: + timeout = endtime - _time() + if timeout <= 0: + break else: self.__value = self.__value - 1 if __debug__: