Development of an internal social media platform with personalised dashboards for students
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

synchronize.py 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  1. #
  2. # Module implementing synchronization primitives
  3. #
  4. # multiprocessing/synchronize.py
  5. #
  6. # Copyright (c) 2006-2008, R Oudkerk
  7. # Licensed to PSF under a Contributor Agreement.
  8. #
  9. from __future__ import absolute_import
  10. import itertools
  11. import os
  12. import signal
  13. import sys
  14. import threading
  15. from ._ext import _billiard, ensure_SemLock
  16. from .five import range, monotonic
  17. from .process import current_process
  18. from .util import Finalize, register_after_fork, debug
  19. from .forking import assert_spawning, Popen
  20. from .compat import bytes, closerange
  21. __all__ = [
  22. 'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore', 'Condition', 'Event',
  23. ]
  24. # Try to import the mp.synchronize module cleanly, if it fails
  25. # raise ImportError for platforms lacking a working sem_open implementation.
  26. # See issue 3770
  27. ensure_SemLock()
  28. #
  29. # Constants
  30. #
  31. RECURSIVE_MUTEX, SEMAPHORE = list(range(2))
  32. SEM_VALUE_MAX = _billiard.SemLock.SEM_VALUE_MAX
  33. try:
  34. sem_unlink = _billiard.SemLock.sem_unlink
  35. except AttributeError: # pragma: no cover
  36. try:
  37. # Py3.4+ implements sem_unlink and the semaphore must be named
  38. from _multiprocessing import sem_unlink # noqa
  39. except ImportError:
  40. sem_unlink = None # noqa
  41. #
  42. # Base class for semaphores and mutexes; wraps `_billiard.SemLock`
  43. #
  44. def _semname(sl):
  45. try:
  46. return sl.name
  47. except AttributeError:
  48. pass
  49. class SemLock(object):
  50. _counter = itertools.count()
  51. def __init__(self, kind, value, maxvalue):
  52. from .forking import _forking_is_enabled
  53. unlink_immediately = _forking_is_enabled or sys.platform == 'win32'
  54. if sem_unlink:
  55. sl = self._semlock = _billiard.SemLock(
  56. kind, value, maxvalue, self._make_name(), unlink_immediately)
  57. else:
  58. sl = self._semlock = _billiard.SemLock(kind, value, maxvalue)
  59. debug('created semlock with handle %s', sl.handle)
  60. self._make_methods()
  61. if sem_unlink:
  62. if sys.platform != 'win32':
  63. def _after_fork(obj):
  64. obj._semlock._after_fork()
  65. register_after_fork(self, _after_fork)
  66. if _semname(self._semlock) is not None:
  67. # We only get here if we are on Unix with forking
  68. # disabled. When the object is garbage collected or the
  69. # process shuts down we unlink the semaphore name
  70. Finalize(self, sem_unlink, (self._semlock.name,),
  71. exitpriority=0)
  72. # In case of abnormal termination unlink semaphore name
  73. _cleanup_semaphore_if_leaked(self._semlock.name)
  74. def _make_methods(self):
  75. self.acquire = self._semlock.acquire
  76. self.release = self._semlock.release
  77. def __enter__(self):
  78. return self._semlock.__enter__()
  79. def __exit__(self, *args):
  80. return self._semlock.__exit__(*args)
  81. def __getstate__(self):
  82. assert_spawning(self)
  83. sl = self._semlock
  84. state = (Popen.duplicate_for_child(sl.handle), sl.kind, sl.maxvalue)
  85. try:
  86. state += (sl.name, )
  87. except AttributeError:
  88. pass
  89. return state
  90. def __setstate__(self, state):
  91. self._semlock = _billiard.SemLock._rebuild(*state)
  92. debug('recreated blocker with handle %r', state[0])
  93. self._make_methods()
  94. @staticmethod
  95. def _make_name():
  96. return '/%s-%s-%s' % (current_process()._semprefix,
  97. os.getpid(), next(SemLock._counter))
  98. class Semaphore(SemLock):
  99. def __init__(self, value=1):
  100. SemLock.__init__(self, SEMAPHORE, value, SEM_VALUE_MAX)
  101. def get_value(self):
  102. return self._semlock._get_value()
  103. def __repr__(self):
  104. try:
  105. value = self._semlock._get_value()
  106. except Exception:
  107. value = 'unknown'
  108. return '<Semaphore(value=%s)>' % value
  109. class BoundedSemaphore(Semaphore):
  110. def __init__(self, value=1):
  111. SemLock.__init__(self, SEMAPHORE, value, value)
  112. def __repr__(self):
  113. try:
  114. value = self._semlock._get_value()
  115. except Exception:
  116. value = 'unknown'
  117. return '<BoundedSemaphore(value=%s, maxvalue=%s)>' % \
  118. (value, self._semlock.maxvalue)
  119. class Lock(SemLock):
  120. '''
  121. Non-recursive lock.
  122. '''
  123. def __init__(self):
  124. SemLock.__init__(self, SEMAPHORE, 1, 1)
  125. def __repr__(self):
  126. try:
  127. if self._semlock._is_mine():
  128. name = current_process().name
  129. if threading.currentThread().name != 'MainThread':
  130. name += '|' + threading.currentThread().name
  131. elif self._semlock._get_value() == 1:
  132. name = 'None'
  133. elif self._semlock._count() > 0:
  134. name = 'SomeOtherThread'
  135. else:
  136. name = 'SomeOtherProcess'
  137. except Exception:
  138. name = 'unknown'
  139. return '<Lock(owner=%s)>' % name
  140. class RLock(SemLock):
  141. '''
  142. Recursive lock
  143. '''
  144. def __init__(self):
  145. SemLock.__init__(self, RECURSIVE_MUTEX, 1, 1)
  146. def __repr__(self):
  147. try:
  148. if self._semlock._is_mine():
  149. name = current_process().name
  150. if threading.currentThread().name != 'MainThread':
  151. name += '|' + threading.currentThread().name
  152. count = self._semlock._count()
  153. elif self._semlock._get_value() == 1:
  154. name, count = 'None', 0
  155. elif self._semlock._count() > 0:
  156. name, count = 'SomeOtherThread', 'nonzero'
  157. else:
  158. name, count = 'SomeOtherProcess', 'nonzero'
  159. except Exception:
  160. name, count = 'unknown', 'unknown'
  161. return '<RLock(%s, %s)>' % (name, count)
  162. class Condition(object):
  163. '''
  164. Condition variable
  165. '''
  166. def __init__(self, lock=None):
  167. self._lock = lock or RLock()
  168. self._sleeping_count = Semaphore(0)
  169. self._woken_count = Semaphore(0)
  170. self._wait_semaphore = Semaphore(0)
  171. self._make_methods()
  172. def __getstate__(self):
  173. assert_spawning(self)
  174. return (self._lock, self._sleeping_count,
  175. self._woken_count, self._wait_semaphore)
  176. def __setstate__(self, state):
  177. (self._lock, self._sleeping_count,
  178. self._woken_count, self._wait_semaphore) = state
  179. self._make_methods()
  180. def __enter__(self):
  181. return self._lock.__enter__()
  182. def __exit__(self, *args):
  183. return self._lock.__exit__(*args)
  184. def _make_methods(self):
  185. self.acquire = self._lock.acquire
  186. self.release = self._lock.release
  187. def __repr__(self):
  188. try:
  189. num_waiters = (self._sleeping_count._semlock._get_value() -
  190. self._woken_count._semlock._get_value())
  191. except Exception:
  192. num_waiters = 'unkown'
  193. return '<Condition(%s, %s)>' % (self._lock, num_waiters)
  194. def wait(self, timeout=None):
  195. assert self._lock._semlock._is_mine(), \
  196. 'must acquire() condition before using wait()'
  197. # indicate that this thread is going to sleep
  198. self._sleeping_count.release()
  199. # release lock
  200. count = self._lock._semlock._count()
  201. for i in range(count):
  202. self._lock.release()
  203. try:
  204. # wait for notification or timeout
  205. ret = self._wait_semaphore.acquire(True, timeout)
  206. finally:
  207. # indicate that this thread has woken
  208. self._woken_count.release()
  209. # reacquire lock
  210. for i in range(count):
  211. self._lock.acquire()
  212. return ret
  213. def notify(self):
  214. assert self._lock._semlock._is_mine(), 'lock is not owned'
  215. assert not self._wait_semaphore.acquire(False)
  216. # to take account of timeouts since last notify() we subtract
  217. # woken_count from sleeping_count and rezero woken_count
  218. while self._woken_count.acquire(False):
  219. res = self._sleeping_count.acquire(False)
  220. assert res
  221. if self._sleeping_count.acquire(False): # try grabbing a sleeper
  222. self._wait_semaphore.release() # wake up one sleeper
  223. self._woken_count.acquire() # wait for sleeper to wake
  224. # rezero _wait_semaphore in case a timeout just happened
  225. self._wait_semaphore.acquire(False)
  226. def notify_all(self):
  227. assert self._lock._semlock._is_mine(), 'lock is not owned'
  228. assert not self._wait_semaphore.acquire(False)
  229. # to take account of timeouts since last notify*() we subtract
  230. # woken_count from sleeping_count and rezero woken_count
  231. while self._woken_count.acquire(False):
  232. res = self._sleeping_count.acquire(False)
  233. assert res
  234. sleepers = 0
  235. while self._sleeping_count.acquire(False):
  236. self._wait_semaphore.release() # wake up one sleeper
  237. sleepers += 1
  238. if sleepers:
  239. for i in range(sleepers):
  240. self._woken_count.acquire() # wait for a sleeper to wake
  241. # rezero wait_semaphore in case some timeouts just happened
  242. while self._wait_semaphore.acquire(False):
  243. pass
  244. def wait_for(self, predicate, timeout=None):
  245. result = predicate()
  246. if result:
  247. return result
  248. if timeout is not None:
  249. endtime = monotonic() + timeout
  250. else:
  251. endtime = None
  252. waittime = None
  253. while not result:
  254. if endtime is not None:
  255. waittime = endtime - monotonic()
  256. if waittime <= 0:
  257. break
  258. self.wait(waittime)
  259. result = predicate()
  260. return result
  261. class Event(object):
  262. def __init__(self):
  263. self._cond = Condition(Lock())
  264. self._flag = Semaphore(0)
  265. def is_set(self):
  266. self._cond.acquire()
  267. try:
  268. if self._flag.acquire(False):
  269. self._flag.release()
  270. return True
  271. return False
  272. finally:
  273. self._cond.release()
  274. def set(self):
  275. self._cond.acquire()
  276. try:
  277. self._flag.acquire(False)
  278. self._flag.release()
  279. self._cond.notify_all()
  280. finally:
  281. self._cond.release()
  282. def clear(self):
  283. self._cond.acquire()
  284. try:
  285. self._flag.acquire(False)
  286. finally:
  287. self._cond.release()
  288. def wait(self, timeout=None):
  289. self._cond.acquire()
  290. try:
  291. if self._flag.acquire(False):
  292. self._flag.release()
  293. else:
  294. self._cond.wait(timeout)
  295. if self._flag.acquire(False):
  296. self._flag.release()
  297. return True
  298. return False
  299. finally:
  300. self._cond.release()
  301. if sys.platform != 'win32':
  302. #
  303. # Protection against unlinked semaphores if the program ends abnormally
  304. # and forking has been disabled.
  305. #
  306. def _cleanup_semaphore_if_leaked(name):
  307. name = name.encode('ascii') + bytes('\0', 'ascii')
  308. if len(name) > 512:
  309. # posix guarantees that writes to a pipe of less than PIPE_BUF
  310. # bytes are atomic, and that PIPE_BUF >= 512
  311. raise ValueError('name too long')
  312. fd = _get_unlinkfd()
  313. bits = os.write(fd, name)
  314. assert bits == len(name)
  315. def _get_unlinkfd():
  316. cp = current_process()
  317. if cp._unlinkfd is None:
  318. r, w = os.pipe()
  319. pid = os.fork()
  320. if pid == 0:
  321. try:
  322. from setproctitle import setproctitle
  323. setproctitle("[sem_cleanup for %r]" % cp.pid)
  324. except:
  325. pass
  326. # Fork a process which will survive until all other processes
  327. # which have a copy of the write end of the pipe have exited.
  328. # The forked process just collects names of semaphores until
  329. # EOF is indicated. Then it tries unlinking all the names it
  330. # has collected.
  331. _collect_names_then_unlink(r)
  332. os._exit(0)
  333. os.close(r)
  334. cp._unlinkfd = w
  335. return cp._unlinkfd
  336. def _collect_names_then_unlink(r):
  337. # protect the process from ^C and "killall python" etc
  338. signal.signal(signal.SIGINT, signal.SIG_IGN)
  339. signal.signal(signal.SIGTERM, signal.SIG_IGN)
  340. # close all fds except r
  341. try:
  342. MAXFD = os.sysconf("SC_OPEN_MAX")
  343. except:
  344. MAXFD = 256
  345. closerange(0, r)
  346. closerange(r + 1, MAXFD)
  347. # collect data written to pipe
  348. data = []
  349. while 1:
  350. try:
  351. s = os.read(r, 512)
  352. except:
  353. # XXX IO lock might be held at fork, so don't try
  354. # printing unexpected exception - see issue 6721
  355. pass
  356. else:
  357. if not s:
  358. break
  359. data.append(s)
  360. # attempt to unlink each collected name
  361. for name in bytes('', 'ascii').join(data).split(bytes('\0', 'ascii')):
  362. try:
  363. sem_unlink(name.decode('ascii'))
  364. except:
  365. # XXX IO lock might be held at fork, so don't try
  366. # printing unexpected exception - see issue 6721
  367. pass