/Lib/multiprocessing/synchronize.py

http://unladen-swallow.googlecode.com/ · Python · 305 lines · 201 code · 59 blank · 45 comment · 46 complexity · 41198b8850bd8b57de58711656324dd7 MD5 · raw file

  1. #
  2. # Module implementing synchronization primitives
  3. #
  4. # multiprocessing/synchronize.py
  5. #
  6. # Copyright (c) 2006-2008, R Oudkerk --- see COPYING.txt
  7. #
  8. __all__ = [
  9. 'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore', 'Condition', 'Event'
  10. ]
  11. import threading
  12. import os
  13. import sys
  14. from time import time as _time, sleep as _sleep
  15. import _multiprocessing
  16. from multiprocessing.process import current_process
  17. from multiprocessing.util import Finalize, register_after_fork, debug
  18. from multiprocessing.forking import assert_spawning, Popen
  19. # Try to import the mp.synchronize module cleanly, if it fails
  20. # raise ImportError for platforms lacking a working sem_open implementation.
  21. # See issue 3770
  22. try:
  23. from _multiprocessing import SemLock
  24. except (ImportError):
  25. raise ImportError("This platform lacks a functioning sem_open" +
  26. " implementation, therefore, the required" +
  27. " synchronization primitives needed will not" +
  28. " function, see issue 3770.")
  29. #
  30. # Constants
  31. #
  32. RECURSIVE_MUTEX, SEMAPHORE = range(2)
  33. SEM_VALUE_MAX = _multiprocessing.SemLock.SEM_VALUE_MAX
  34. #
  35. # Base class for semaphores and mutexes; wraps `_multiprocessing.SemLock`
  36. #
  37. class SemLock(object):
  38. def __init__(self, kind, value, maxvalue):
  39. sl = self._semlock = _multiprocessing.SemLock(kind, value, maxvalue)
  40. debug('created semlock with handle %s' % sl.handle)
  41. self._make_methods()
  42. if sys.platform != 'win32':
  43. def _after_fork(obj):
  44. obj._semlock._after_fork()
  45. register_after_fork(self, _after_fork)
  46. def _make_methods(self):
  47. self.acquire = self._semlock.acquire
  48. self.release = self._semlock.release
  49. self.__enter__ = self._semlock.__enter__
  50. self.__exit__ = self._semlock.__exit__
  51. def __getstate__(self):
  52. assert_spawning(self)
  53. sl = self._semlock
  54. return (Popen.duplicate_for_child(sl.handle), sl.kind, sl.maxvalue)
  55. def __setstate__(self, state):
  56. self._semlock = _multiprocessing.SemLock._rebuild(*state)
  57. debug('recreated blocker with handle %r' % state[0])
  58. self._make_methods()
  59. #
  60. # Semaphore
  61. #
  62. class Semaphore(SemLock):
  63. def __init__(self, value=1):
  64. SemLock.__init__(self, SEMAPHORE, value, SEM_VALUE_MAX)
  65. def get_value(self):
  66. return self._semlock._get_value()
  67. def __repr__(self):
  68. try:
  69. value = self._semlock._get_value()
  70. except Exception:
  71. value = 'unknown'
  72. return '<Semaphore(value=%s)>' % value
  73. #
  74. # Bounded semaphore
  75. #
  76. class BoundedSemaphore(Semaphore):
  77. def __init__(self, value=1):
  78. SemLock.__init__(self, SEMAPHORE, value, value)
  79. def __repr__(self):
  80. try:
  81. value = self._semlock._get_value()
  82. except Exception:
  83. value = 'unknown'
  84. return '<BoundedSemaphore(value=%s, maxvalue=%s)>' % \
  85. (value, self._semlock.maxvalue)
  86. #
  87. # Non-recursive lock
  88. #
  89. class Lock(SemLock):
  90. def __init__(self):
  91. SemLock.__init__(self, SEMAPHORE, 1, 1)
  92. def __repr__(self):
  93. try:
  94. if self._semlock._is_mine():
  95. name = current_process().name
  96. if threading.current_thread().name != 'MainThread':
  97. name += '|' + threading.current_thread().name
  98. elif self._semlock._get_value() == 1:
  99. name = 'None'
  100. elif self._semlock._count() > 0:
  101. name = 'SomeOtherThread'
  102. else:
  103. name = 'SomeOtherProcess'
  104. except Exception:
  105. name = 'unknown'
  106. return '<Lock(owner=%s)>' % name
  107. #
  108. # Recursive lock
  109. #
  110. class RLock(SemLock):
  111. def __init__(self):
  112. SemLock.__init__(self, RECURSIVE_MUTEX, 1, 1)
  113. def __repr__(self):
  114. try:
  115. if self._semlock._is_mine():
  116. name = current_process().name
  117. if threading.current_thread().name != 'MainThread':
  118. name += '|' + threading.current_thread().name
  119. count = self._semlock._count()
  120. elif self._semlock._get_value() == 1:
  121. name, count = 'None', 0
  122. elif self._semlock._count() > 0:
  123. name, count = 'SomeOtherThread', 'nonzero'
  124. else:
  125. name, count = 'SomeOtherProcess', 'nonzero'
  126. except Exception:
  127. name, count = 'unknown', 'unknown'
  128. return '<RLock(%s, %s)>' % (name, count)
  129. #
  130. # Condition variable
  131. #
  132. class Condition(object):
  133. def __init__(self, lock=None):
  134. self._lock = lock or RLock()
  135. self._sleeping_count = Semaphore(0)
  136. self._woken_count = Semaphore(0)
  137. self._wait_semaphore = Semaphore(0)
  138. self._make_methods()
  139. def __getstate__(self):
  140. assert_spawning(self)
  141. return (self._lock, self._sleeping_count,
  142. self._woken_count, self._wait_semaphore)
  143. def __setstate__(self, state):
  144. (self._lock, self._sleeping_count,
  145. self._woken_count, self._wait_semaphore) = state
  146. self._make_methods()
  147. def _make_methods(self):
  148. self.acquire = self._lock.acquire
  149. self.release = self._lock.release
  150. self.__enter__ = self._lock.__enter__
  151. self.__exit__ = self._lock.__exit__
  152. def __repr__(self):
  153. try:
  154. num_waiters = (self._sleeping_count._semlock._get_value() -
  155. self._woken_count._semlock._get_value())
  156. except Exception:
  157. num_waiters = 'unkown'
  158. return '<Condition(%s, %s)>' % (self._lock, num_waiters)
  159. def wait(self, timeout=None):
  160. assert self._lock._semlock._is_mine(), \
  161. 'must acquire() condition before using wait()'
  162. # indicate that this thread is going to sleep
  163. self._sleeping_count.release()
  164. # release lock
  165. count = self._lock._semlock._count()
  166. for i in xrange(count):
  167. self._lock.release()
  168. try:
  169. # wait for notification or timeout
  170. self._wait_semaphore.acquire(True, timeout)
  171. finally:
  172. # indicate that this thread has woken
  173. self._woken_count.release()
  174. # reacquire lock
  175. for i in xrange(count):
  176. self._lock.acquire()
  177. def notify(self):
  178. assert self._lock._semlock._is_mine(), 'lock is not owned'
  179. assert not self._wait_semaphore.acquire(False)
  180. # to take account of timeouts since last notify() we subtract
  181. # woken_count from sleeping_count and rezero woken_count
  182. while self._woken_count.acquire(False):
  183. res = self._sleeping_count.acquire(False)
  184. assert res
  185. if self._sleeping_count.acquire(False): # try grabbing a sleeper
  186. self._wait_semaphore.release() # wake up one sleeper
  187. self._woken_count.acquire() # wait for the sleeper to wake
  188. # rezero _wait_semaphore in case a timeout just happened
  189. self._wait_semaphore.acquire(False)
  190. def notify_all(self):
  191. assert self._lock._semlock._is_mine(), 'lock is not owned'
  192. assert not self._wait_semaphore.acquire(False)
  193. # to take account of timeouts since last notify*() we subtract
  194. # woken_count from sleeping_count and rezero woken_count
  195. while self._woken_count.acquire(False):
  196. res = self._sleeping_count.acquire(False)
  197. assert res
  198. sleepers = 0
  199. while self._sleeping_count.acquire(False):
  200. self._wait_semaphore.release() # wake up one sleeper
  201. sleepers += 1
  202. if sleepers:
  203. for i in xrange(sleepers):
  204. self._woken_count.acquire() # wait for a sleeper to wake
  205. # rezero wait_semaphore in case some timeouts just happened
  206. while self._wait_semaphore.acquire(False):
  207. pass
  208. #
  209. # Event
  210. #
  211. class Event(object):
  212. def __init__(self):
  213. self._cond = Condition(Lock())
  214. self._flag = Semaphore(0)
  215. def is_set(self):
  216. self._cond.acquire()
  217. try:
  218. if self._flag.acquire(False):
  219. self._flag.release()
  220. return True
  221. return False
  222. finally:
  223. self._cond.release()
  224. def set(self):
  225. self._cond.acquire()
  226. try:
  227. self._flag.acquire(False)
  228. self._flag.release()
  229. self._cond.notify_all()
  230. finally:
  231. self._cond.release()
  232. def clear(self):
  233. self._cond.acquire()
  234. try:
  235. self._flag.acquire(False)
  236. finally:
  237. self._cond.release()
  238. def wait(self, timeout=None):
  239. self._cond.acquire()
  240. try:
  241. if self._flag.acquire(False):
  242. self._flag.release()
  243. else:
  244. self._cond.wait(timeout)
  245. finally:
  246. self._cond.release()