您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

316 行
12KB

  1. import collections
  2. import eventlet
  3. from eventlet import hubs
  4. class Semaphore(object):
  5. """An unbounded semaphore.
  6. Optionally initialize with a resource *count*, then :meth:`acquire` and
  7. :meth:`release` resources as needed. Attempting to :meth:`acquire` when
  8. *count* is zero suspends the calling greenthread until *count* becomes
  9. nonzero again.
  10. This is API-compatible with :class:`threading.Semaphore`.
  11. It is a context manager, and thus can be used in a with block::
  12. sem = Semaphore(2)
  13. with sem:
  14. do_some_stuff()
  15. If not specified, *value* defaults to 1.
  16. It is possible to limit acquire time::
  17. sem = Semaphore()
  18. ok = sem.acquire(timeout=0.1)
  19. # True if acquired, False if timed out.
  20. """
  21. def __init__(self, value=1):
  22. try:
  23. value = int(value)
  24. except ValueError as e:
  25. msg = 'Semaphore() expect value :: int, actual: {0} {1}'.format(type(value), str(e))
  26. raise TypeError(msg)
  27. if value < 0:
  28. msg = 'Semaphore() expect value >= 0, actual: {0}'.format(repr(value))
  29. raise ValueError(msg)
  30. self.counter = value
  31. self._waiters = collections.deque()
  32. def __repr__(self):
  33. params = (self.__class__.__name__, hex(id(self)),
  34. self.counter, len(self._waiters))
  35. return '<%s at %s c=%s _w[%s]>' % params
  36. def __str__(self):
  37. params = (self.__class__.__name__, self.counter, len(self._waiters))
  38. return '<%s c=%s _w[%s]>' % params
  39. def locked(self):
  40. """Returns true if a call to acquire would block.
  41. """
  42. return self.counter <= 0
  43. def bounded(self):
  44. """Returns False; for consistency with
  45. :class:`~eventlet.semaphore.CappedSemaphore`.
  46. """
  47. return False
  48. def acquire(self, blocking=True, timeout=None):
  49. """Acquire a semaphore.
  50. When invoked without arguments: if the internal counter is larger than
  51. zero on entry, decrement it by one and return immediately. If it is zero
  52. on entry, block, waiting until some other thread has called release() to
  53. make it larger than zero. This is done with proper interlocking so that
  54. if multiple acquire() calls are blocked, release() will wake exactly one
  55. of them up. The implementation may pick one at random, so the order in
  56. which blocked threads are awakened should not be relied on. There is no
  57. return value in this case.
  58. When invoked with blocking set to true, do the same thing as when called
  59. without arguments, and return true.
  60. When invoked with blocking set to false, do not block. If a call without
  61. an argument would block, return false immediately; otherwise, do the
  62. same thing as when called without arguments, and return true.
  63. Timeout value must be strictly positive.
  64. """
  65. if timeout == -1:
  66. timeout = None
  67. if timeout is not None and timeout < 0:
  68. raise ValueError("timeout value must be strictly positive")
  69. if not blocking:
  70. if timeout is not None:
  71. raise ValueError("can't specify timeout for non-blocking acquire")
  72. timeout = 0
  73. if not blocking and self.locked():
  74. return False
  75. current_thread = eventlet.getcurrent()
  76. if self.counter <= 0 or self._waiters:
  77. if current_thread not in self._waiters:
  78. self._waiters.append(current_thread)
  79. try:
  80. if timeout is not None:
  81. ok = False
  82. with eventlet.Timeout(timeout, False):
  83. while self.counter <= 0:
  84. hubs.get_hub().switch()
  85. ok = True
  86. if not ok:
  87. return False
  88. else:
  89. # If someone else is already in this wait loop, give them
  90. # a chance to get out.
  91. while True:
  92. hubs.get_hub().switch()
  93. if self.counter > 0:
  94. break
  95. finally:
  96. try:
  97. self._waiters.remove(current_thread)
  98. except ValueError:
  99. # Fine if its already been dropped.
  100. pass
  101. self.counter -= 1
  102. return True
  103. def __enter__(self):
  104. self.acquire()
  105. def release(self, blocking=True):
  106. """Release a semaphore, incrementing the internal counter by one. When
  107. it was zero on entry and another thread is waiting for it to become
  108. larger than zero again, wake up that thread.
  109. The *blocking* argument is for consistency with CappedSemaphore and is
  110. ignored
  111. """
  112. self.counter += 1
  113. if self._waiters:
  114. hubs.get_hub().schedule_call_global(0, self._do_acquire)
  115. return True
  116. def _do_acquire(self):
  117. if self._waiters and self.counter > 0:
  118. waiter = self._waiters.popleft()
  119. waiter.switch()
  120. def __exit__(self, typ, val, tb):
  121. self.release()
  122. @property
  123. def balance(self):
  124. """An integer value that represents how many new calls to
  125. :meth:`acquire` or :meth:`release` would be needed to get the counter to
  126. 0. If it is positive, then its value is the number of acquires that can
  127. happen before the next acquire would block. If it is negative, it is
  128. the negative of the number of releases that would be required in order
  129. to make the counter 0 again (one more release would push the counter to
  130. 1 and unblock acquirers). It takes into account how many greenthreads
  131. are currently blocking in :meth:`acquire`.
  132. """
  133. # positive means there are free items
  134. # zero means there are no free items but nobody has requested one
  135. # negative means there are requests for items, but no items
  136. return self.counter - len(self._waiters)
  137. class BoundedSemaphore(Semaphore):
  138. """A bounded semaphore checks to make sure its current value doesn't exceed
  139. its initial value. If it does, ValueError is raised. In most situations
  140. semaphores are used to guard resources with limited capacity. If the
  141. semaphore is released too many times it's a sign of a bug. If not given,
  142. *value* defaults to 1.
  143. """
  144. def __init__(self, value=1):
  145. super(BoundedSemaphore, self).__init__(value)
  146. self.original_counter = value
  147. def release(self, blocking=True):
  148. """Release a semaphore, incrementing the internal counter by one. If
  149. the counter would exceed the initial value, raises ValueError. When
  150. it was zero on entry and another thread is waiting for it to become
  151. larger than zero again, wake up that thread.
  152. The *blocking* argument is for consistency with :class:`CappedSemaphore`
  153. and is ignored
  154. """
  155. if self.counter >= self.original_counter:
  156. raise ValueError("Semaphore released too many times")
  157. return super(BoundedSemaphore, self).release(blocking)
  158. class CappedSemaphore(object):
  159. """A blockingly bounded semaphore.
  160. Optionally initialize with a resource *count*, then :meth:`acquire` and
  161. :meth:`release` resources as needed. Attempting to :meth:`acquire` when
  162. *count* is zero suspends the calling greenthread until count becomes nonzero
  163. again. Attempting to :meth:`release` after *count* has reached *limit*
  164. suspends the calling greenthread until *count* becomes less than *limit*
  165. again.
  166. This has the same API as :class:`threading.Semaphore`, though its
  167. semantics and behavior differ subtly due to the upper limit on calls
  168. to :meth:`release`. It is **not** compatible with
  169. :class:`threading.BoundedSemaphore` because it blocks when reaching *limit*
  170. instead of raising a ValueError.
  171. It is a context manager, and thus can be used in a with block::
  172. sem = CappedSemaphore(2)
  173. with sem:
  174. do_some_stuff()
  175. """
  176. def __init__(self, count, limit):
  177. if count < 0:
  178. raise ValueError("CappedSemaphore must be initialized with a "
  179. "positive number, got %s" % count)
  180. if count > limit:
  181. # accidentally, this also catches the case when limit is None
  182. raise ValueError("'count' cannot be more than 'limit'")
  183. self.lower_bound = Semaphore(count)
  184. self.upper_bound = Semaphore(limit - count)
  185. def __repr__(self):
  186. params = (self.__class__.__name__, hex(id(self)),
  187. self.balance, self.lower_bound, self.upper_bound)
  188. return '<%s at %s b=%s l=%s u=%s>' % params
  189. def __str__(self):
  190. params = (self.__class__.__name__, self.balance,
  191. self.lower_bound, self.upper_bound)
  192. return '<%s b=%s l=%s u=%s>' % params
  193. def locked(self):
  194. """Returns true if a call to acquire would block.
  195. """
  196. return self.lower_bound.locked()
  197. def bounded(self):
  198. """Returns true if a call to release would block.
  199. """
  200. return self.upper_bound.locked()
  201. def acquire(self, blocking=True):
  202. """Acquire a semaphore.
  203. When invoked without arguments: if the internal counter is larger than
  204. zero on entry, decrement it by one and return immediately. If it is zero
  205. on entry, block, waiting until some other thread has called release() to
  206. make it larger than zero. This is done with proper interlocking so that
  207. if multiple acquire() calls are blocked, release() will wake exactly one
  208. of them up. The implementation may pick one at random, so the order in
  209. which blocked threads are awakened should not be relied on. There is no
  210. return value in this case.
  211. When invoked with blocking set to true, do the same thing as when called
  212. without arguments, and return true.
  213. When invoked with blocking set to false, do not block. If a call without
  214. an argument would block, return false immediately; otherwise, do the
  215. same thing as when called without arguments, and return true.
  216. """
  217. if not blocking and self.locked():
  218. return False
  219. self.upper_bound.release()
  220. try:
  221. return self.lower_bound.acquire()
  222. except:
  223. self.upper_bound.counter -= 1
  224. # using counter directly means that it can be less than zero.
  225. # however I certainly don't need to wait here and I don't seem to have
  226. # a need to care about such inconsistency
  227. raise
  228. def __enter__(self):
  229. self.acquire()
  230. def release(self, blocking=True):
  231. """Release a semaphore. In this class, this behaves very much like
  232. an :meth:`acquire` but in the opposite direction.
  233. Imagine the docs of :meth:`acquire` here, but with every direction
  234. reversed. When calling this method, it will block if the internal
  235. counter is greater than or equal to *limit*.
  236. """
  237. if not blocking and self.bounded():
  238. return False
  239. self.lower_bound.release()
  240. try:
  241. return self.upper_bound.acquire()
  242. except:
  243. self.lower_bound.counter -= 1
  244. raise
  245. def __exit__(self, typ, val, tb):
  246. self.release()
  247. @property
  248. def balance(self):
  249. """An integer value that represents how many new calls to
  250. :meth:`acquire` or :meth:`release` would be needed to get the counter to
  251. 0. If it is positive, then its value is the number of acquires that can
  252. happen before the next acquire would block. If it is negative, it is
  253. the negative of the number of releases that would be required in order
  254. to make the counter 0 again (one more release would push the counter to
  255. 1 and unblock acquirers). It takes into account how many greenthreads
  256. are currently blocking in :meth:`acquire` and :meth:`release`.
  257. """
  258. return self.lower_bound.balance - self.upper_bound.balance