]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
Add tornado.locks.BoundedSemaphore. 1352/head
authorA. Jesse Jiryu Davis <jesse@mongodb.com>
Fri, 27 Feb 2015 03:11:00 +0000 (22:11 -0500)
committerA. Jesse Jiryu Davis <jesse@mongodb.com>
Fri, 27 Feb 2015 03:11:00 +0000 (22:11 -0500)
tornado/locks.py
tornado/test/locks_test.py

index 49667cead2472cf11a950e7c4a7b4612a3e2df64..1367e1de89e8a25762a8b4c30067f199592be1d4 100644 (file)
@@ -14,7 +14,7 @@
 
 from __future__ import absolute_import, division, print_function, with_statement
 
-__all__ = ['Condition', 'Event', 'Semaphore']
+__all__ = ['Condition', 'Event', 'Semaphore', 'BoundedSemaphore']
 
 import collections
 
@@ -223,3 +223,21 @@ class Semaphore(object):
             " 'with semaphore'")
 
     __exit__ = __enter__
+
+
+class BoundedSemaphore(Semaphore):
+    """A semaphore that prevents release() being called too many times.
+
+    If `.release` would increment the semaphore's value past the initial
+    value, it raises `ValueError`. Semaphores are mostly used to guard
+    resources with limited capacity, so a semaphore released too many times
+    is a sign of a bug.
+    """
+    def __init__(self, value=1):
+        super(BoundedSemaphore, self).__init__(value=value)
+        self._initial_value = value
+
+    def release(self):
+        if self._value >= self._initial_value:
+            raise ValueError("Semaphore released too many times")
+        super(BoundedSemaphore, self).release()
index 38d2bafb8b194a59cd2934087da9bc2eaab2a675..29d80b94fd29cbcc7cc2e33bebfff82da34516b0 100644 (file)
@@ -349,5 +349,20 @@ class SemaphoreContextManagerTest(AsyncTestCase):
                 pass
 
 
+class BoundedSemaphoreTest(AsyncTestCase):
+    def test_release_unacquired(self):
+        sem = locks.BoundedSemaphore()
+        self.assertRaises(ValueError, sem.release)
+        # Value is 0.
+        sem.acquire()
+        # Block on acquire().
+        future = sem.acquire()
+        self.assertFalse(future.done())
+        sem.release()
+        self.assertTrue(future.done())
+        # Value is 1.
+        sem.release()
+        self.assertRaises(ValueError, sem.release)
+
 if __name__ == '__main__':
     unittest.main()