from __future__ import with_statement
+
+import collections
+
from eventlet import greenthread
from eventlet import hubs
from eventlet.timeout import Timeout
if value < 0:
raise ValueError("Semaphore must be initialized with a positive "
"number, got %s" % value)
- self._waiters = set()
+ self._waiters = collections.deque()
def __repr__(self):
params = (self.__class__.__name__, hex(id(self)),
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.
+
+ Timeout value must be strictly positive.
"""
- if not blocking and timeout is not None:
- raise ValueError("can't specify timeout for non-blocking acquire")
+ if timeout == -1:
+ timeout = None
+ if timeout is not None and timeout < 0:
+ raise ValueError("timeout value must be strictly positive")
+ if not blocking:
+ if timeout is not None:
+ raise ValueError("can't specify timeout for non-blocking acquire")
+ timeout = 0
if not blocking and self.locked():
return False
- if self.counter <= 0:
- self._waiters.add(greenthread.getcurrent())
+
+ current_thread = greenthread.getcurrent()
+
+ if self.counter <= 0 or self._waiters:
+ if current_thread not in self._waiters:
+ self._waiters.append(current_thread)
try:
if timeout is not None:
ok = False
if not ok:
return False
else:
- while self.counter <= 0:
+ # If someone else is already in this wait loop, give them
+ # a chance to get out.
+ while True:
hubs.get_hub().switch()
+ if self.counter > 0:
+ break
finally:
- self._waiters.discard(greenthread.getcurrent())
+ try:
+ self._waiters.remove(current_thread)
+ except ValueError:
+ # Fine if its already been dropped.
+ pass
+
self.counter -= 1
return True
def _do_acquire(self):
if self._waiters and self.counter > 0:
- waiter = self._waiters.pop()
+ waiter = self._waiters.popleft()
waiter.switch()
def __exit__(self, typ, val, tb):