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.

pool.py 63KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959
  1. # -*- coding: utf-8 -*-
  2. #
  3. # Module providing the `Pool` class for managing a process pool
  4. #
  5. # multiprocessing/pool.py
  6. #
  7. # Copyright (c) 2006-2008, R Oudkerk
  8. # Licensed to PSF under a Contributor Agreement.
  9. #
  10. from __future__ import absolute_import
  11. #
  12. # Imports
  13. #
  14. import errno
  15. import itertools
  16. import os
  17. import platform
  18. import signal
  19. import sys
  20. import threading
  21. import time
  22. import warnings
  23. from collections import deque
  24. from functools import partial
  25. from . import Event, Process, cpu_count
  26. from . import util
  27. from .common import pickle_loads, reset_signals, restart_state
  28. from .compat import get_errno, send_offset
  29. from .einfo import ExceptionInfo
  30. from .dummy import DummyProcess
  31. from .exceptions import (
  32. CoroStop,
  33. RestartFreqExceeded,
  34. SoftTimeLimitExceeded,
  35. Terminated,
  36. TimeLimitExceeded,
  37. TimeoutError,
  38. WorkerLostError,
  39. )
  40. from .five import Empty, Queue, range, values, reraise, monotonic
  41. from .util import Finalize, debug
  42. PY3 = sys.version_info[0] == 3
  43. if platform.system() == 'Windows': # pragma: no cover
  44. # On Windows os.kill calls TerminateProcess which cannot be
  45. # handled by # any process, so this is needed to terminate the task
  46. # *and its children* (if any).
  47. from ._win import kill_processtree as _kill # noqa
  48. SIGKILL = signal.SIGTERM
  49. else:
  50. from os import kill as _kill # noqa
  51. SIGKILL = signal.SIGKILL
  52. try:
  53. TIMEOUT_MAX = threading.TIMEOUT_MAX
  54. except AttributeError: # pragma: no cover
  55. TIMEOUT_MAX = 1e10 # noqa
  56. if sys.version_info >= (3, 3):
  57. _Semaphore = threading.Semaphore
  58. else:
  59. # Semaphore is a factory function pointing to _Semaphore
  60. _Semaphore = threading._Semaphore # noqa
  61. SIGMAP = dict(
  62. (getattr(signal, n), n) for n in dir(signal) if n.startswith('SIG')
  63. )
  64. #
  65. # Constants representing the state of a pool
  66. #
  67. RUN = 0
  68. CLOSE = 1
  69. TERMINATE = 2
  70. #
  71. # Constants representing the state of a job
  72. #
  73. ACK = 0
  74. READY = 1
  75. TASK = 2
  76. NACK = 3
  77. DEATH = 4
  78. #
  79. # Exit code constants
  80. #
  81. EX_OK = 0
  82. EX_FAILURE = 1
  83. EX_RECYCLE = 0x9B
  84. # Signal used for soft time limits.
  85. SIG_SOFT_TIMEOUT = getattr(signal, "SIGUSR1", None)
  86. #
  87. # Miscellaneous
  88. #
  89. LOST_WORKER_TIMEOUT = 10.0
  90. EX_OK = getattr(os, "EX_OK", 0)
  91. job_counter = itertools.count()
  92. Lock = threading.Lock
  93. def _get_send_offset(connection):
  94. try:
  95. native = connection.send_offset
  96. except AttributeError:
  97. native = None
  98. if native is None:
  99. return partial(send_offset, connection.fileno())
  100. return native
  101. def human_status(status):
  102. if (status or 0) < 0:
  103. try:
  104. return 'signal {0} ({1})'.format(-status, SIGMAP[-status])
  105. except KeyError:
  106. return 'signal {0}'.format(-status)
  107. return 'exitcode {0}'.format(status)
  108. def mapstar(args):
  109. return list(map(*args))
  110. def starmapstar(args):
  111. return list(itertools.starmap(args[0], args[1]))
  112. def error(msg, *args, **kwargs):
  113. if util._logger:
  114. util._logger.error(msg, *args, **kwargs)
  115. def stop_if_not_current(thread, timeout=None):
  116. if thread is not threading.currentThread():
  117. thread.stop(timeout)
  118. class LaxBoundedSemaphore(_Semaphore):
  119. """Semaphore that checks that # release is <= # acquires,
  120. but ignores if # releases >= value."""
  121. def __init__(self, value=1, verbose=None):
  122. if PY3:
  123. _Semaphore.__init__(self, value)
  124. else:
  125. _Semaphore.__init__(self, value, verbose)
  126. self._initial_value = value
  127. def shrink(self):
  128. self._initial_value -= 1
  129. self.acquire()
  130. if PY3:
  131. def release(self):
  132. cond = self._cond
  133. with cond:
  134. if self._value < self._initial_value:
  135. self._value += 1
  136. cond.notify_all()
  137. def clear(self):
  138. while self._value < self._initial_value:
  139. _Semaphore.release(self)
  140. def grow(self):
  141. with self._cond:
  142. self._initial_value += 1
  143. self._value += 1
  144. self._cond.notify()
  145. else:
  146. def grow(self):
  147. cond = self._Semaphore__cond
  148. with cond:
  149. self._initial_value += 1
  150. self._Semaphore__value += 1
  151. cond.notify()
  152. def release(self): # noqa
  153. cond = self._Semaphore__cond
  154. with cond:
  155. if self._Semaphore__value < self._initial_value:
  156. self._Semaphore__value += 1
  157. cond.notifyAll()
  158. def clear(self): # noqa
  159. while self._Semaphore__value < self._initial_value:
  160. _Semaphore.release(self)
  161. #
  162. # Exceptions
  163. #
  164. class MaybeEncodingError(Exception):
  165. """Wraps possible unpickleable errors, so they can be
  166. safely sent through the socket."""
  167. def __init__(self, exc, value):
  168. self.exc = repr(exc)
  169. self.value = repr(value)
  170. super(MaybeEncodingError, self).__init__(self.exc, self.value)
  171. def __repr__(self):
  172. return "<MaybeEncodingError: %s>" % str(self)
  173. def __str__(self):
  174. return "Error sending result: '%r'. Reason: '%r'." % (
  175. self.value, self.exc)
  176. class WorkersJoined(Exception):
  177. """All workers have terminated."""
  178. def soft_timeout_sighandler(signum, frame):
  179. raise SoftTimeLimitExceeded()
  180. #
  181. # Code run by worker processes
  182. #
  183. class Worker(Process):
  184. _controlled_termination = False
  185. _job_terminated = False
  186. def __init__(self, inq, outq, synq=None, initializer=None, initargs=(),
  187. maxtasks=None, sentinel=None, on_exit=None,
  188. sigprotection=True):
  189. assert maxtasks is None or (type(maxtasks) == int and maxtasks > 0)
  190. self.initializer = initializer
  191. self.initargs = initargs
  192. self.maxtasks = maxtasks
  193. self._shutdown = sentinel
  194. self.on_exit = on_exit
  195. self.sigprotection = sigprotection
  196. self.inq, self.outq, self.synq = inq, outq, synq
  197. self._make_shortcuts()
  198. super(Worker, self).__init__()
  199. def __reduce__(self):
  200. return self.__class__, (
  201. self.inq, self.outq, self.synq, self.initializer,
  202. self.initargs, self.maxtasks, self._shutdown,
  203. )
  204. def _make_shortcuts(self):
  205. self.inqW_fd = self.inq._writer.fileno() # inqueue write fd
  206. self.outqR_fd = self.outq._reader.fileno() # outqueue read fd
  207. if self.synq:
  208. self.synqR_fd = self.synq._reader.fileno() # synqueue read fd
  209. self.synqW_fd = self.synq._writer.fileno() # synqueue write fd
  210. self.send_syn_offset = _get_send_offset(self.synq._writer)
  211. else:
  212. self.synqR_fd = self.synqW_fd = self._send_syn_offset = None
  213. self._quick_put = self.inq._writer.send
  214. self._quick_get = self.outq._reader.recv
  215. self.send_job_offset = _get_send_offset(self.inq._writer)
  216. def run(self):
  217. _exit = sys.exit
  218. _exitcode = [None]
  219. def exit(status=None):
  220. _exitcode[0] = status
  221. return _exit()
  222. sys.exit = exit
  223. pid = os.getpid()
  224. self._make_child_methods()
  225. self.after_fork()
  226. self.on_loop_start(pid=pid) # callback on loop start
  227. try:
  228. sys.exit(self.workloop(pid=pid))
  229. except Exception as exc:
  230. error('Pool process %r error: %r', self, exc, exc_info=1)
  231. self._do_exit(pid, _exitcode[0], exc)
  232. finally:
  233. self._do_exit(pid, _exitcode[0], None)
  234. def _do_exit(self, pid, exitcode, exc=None):
  235. if exitcode is None:
  236. exitcode = EX_FAILURE if exc else EX_OK
  237. if self.on_exit is not None:
  238. self.on_exit(pid, exitcode)
  239. if sys.platform != 'win32':
  240. try:
  241. self.outq.put((DEATH, (pid, exitcode)))
  242. time.sleep(1)
  243. finally:
  244. os._exit(exitcode)
  245. else:
  246. os._exit(exitcode)
  247. def on_loop_start(self, pid):
  248. pass
  249. def terminate_controlled(self):
  250. self._controlled_termination = True
  251. self.terminate()
  252. def prepare_result(self, result):
  253. return result
  254. def workloop(self, debug=debug, now=monotonic, pid=None):
  255. pid = pid or os.getpid()
  256. put = self.outq.put
  257. inqW_fd = self.inqW_fd
  258. synqW_fd = self.synqW_fd
  259. maxtasks = self.maxtasks
  260. prepare_result = self.prepare_result
  261. wait_for_job = self.wait_for_job
  262. _wait_for_syn = self.wait_for_syn
  263. def wait_for_syn(jid):
  264. i = 0
  265. while 1:
  266. if i > 60:
  267. error('!!!WAIT FOR ACK TIMEOUT: job:%r fd:%r!!!',
  268. jid, self.synq._reader.fileno(), exc_info=1)
  269. req = _wait_for_syn()
  270. if req:
  271. type_, args = req
  272. if type_ == NACK:
  273. return False
  274. assert type_ == ACK
  275. return True
  276. i += 1
  277. completed = 0
  278. while maxtasks is None or (maxtasks and completed < maxtasks):
  279. req = wait_for_job()
  280. if req:
  281. type_, args_ = req
  282. assert type_ == TASK
  283. job, i, fun, args, kwargs = args_
  284. put((ACK, (job, i, now(), pid, synqW_fd)))
  285. if _wait_for_syn:
  286. confirm = wait_for_syn(job)
  287. if not confirm:
  288. continue # received NACK
  289. try:
  290. result = (True, prepare_result(fun(*args, **kwargs)))
  291. except Exception:
  292. result = (False, ExceptionInfo())
  293. try:
  294. put((READY, (job, i, result, inqW_fd)))
  295. except Exception as exc:
  296. _, _, tb = sys.exc_info()
  297. try:
  298. wrapped = MaybeEncodingError(exc, result[1])
  299. einfo = ExceptionInfo((
  300. MaybeEncodingError, wrapped, tb,
  301. ))
  302. put((READY, (job, i, (False, einfo), inqW_fd)))
  303. finally:
  304. del(tb)
  305. completed += 1
  306. debug('worker exiting after %d tasks', completed)
  307. if maxtasks:
  308. return EX_RECYCLE if completed == maxtasks else EX_FAILURE
  309. return EX_OK
  310. def after_fork(self):
  311. if hasattr(self.inq, '_writer'):
  312. self.inq._writer.close()
  313. if hasattr(self.outq, '_reader'):
  314. self.outq._reader.close()
  315. if self.initializer is not None:
  316. self.initializer(*self.initargs)
  317. # Make sure all exiting signals call finally: blocks.
  318. # This is important for the semaphore to be released.
  319. reset_signals(full=self.sigprotection)
  320. # install signal handler for soft timeouts.
  321. if SIG_SOFT_TIMEOUT is not None:
  322. signal.signal(SIG_SOFT_TIMEOUT, soft_timeout_sighandler)
  323. try:
  324. signal.signal(signal.SIGINT, signal.SIG_IGN)
  325. except AttributeError:
  326. pass
  327. def _make_recv_method(self, conn):
  328. get = conn.get
  329. if hasattr(conn, '_reader'):
  330. _poll = conn._reader.poll
  331. if hasattr(conn, 'get_payload') and conn.get_payload:
  332. get_payload = conn.get_payload
  333. def _recv(timeout, loads=pickle_loads):
  334. return True, loads(get_payload())
  335. else:
  336. def _recv(timeout): # noqa
  337. if _poll(timeout):
  338. return True, get()
  339. return False, None
  340. else:
  341. def _recv(timeout): # noqa
  342. try:
  343. return True, get(timeout=timeout)
  344. except Queue.Empty:
  345. return False, None
  346. return _recv
  347. def _make_child_methods(self, loads=pickle_loads):
  348. self.wait_for_job = self._make_protected_receive(self.inq)
  349. self.wait_for_syn = (self._make_protected_receive(self.synq)
  350. if self.synq else None)
  351. def _make_protected_receive(self, conn):
  352. _receive = self._make_recv_method(conn)
  353. should_shutdown = self._shutdown.is_set if self._shutdown else None
  354. def receive(debug=debug):
  355. if should_shutdown and should_shutdown():
  356. debug('worker got sentinel -- exiting')
  357. raise SystemExit(EX_OK)
  358. try:
  359. ready, req = _receive(1.0)
  360. if not ready:
  361. return None
  362. except (EOFError, IOError) as exc:
  363. if get_errno(exc) == errno.EINTR:
  364. return None # interrupted, maybe by gdb
  365. debug('worker got %s -- exiting', type(exc).__name__)
  366. raise SystemExit(EX_FAILURE)
  367. if req is None:
  368. debug('worker got sentinel -- exiting')
  369. raise SystemExit(EX_FAILURE)
  370. return req
  371. return receive
  372. #
  373. # Class representing a process pool
  374. #
  375. class PoolThread(DummyProcess):
  376. def __init__(self, *args, **kwargs):
  377. DummyProcess.__init__(self)
  378. self._state = RUN
  379. self._was_started = False
  380. self.daemon = True
  381. def run(self):
  382. try:
  383. return self.body()
  384. except RestartFreqExceeded as exc:
  385. error("Thread %r crashed: %r", type(self).__name__, exc,
  386. exc_info=1)
  387. _kill(os.getpid(), signal.SIGTERM)
  388. sys.exit()
  389. except Exception as exc:
  390. error("Thread %r crashed: %r", type(self).__name__, exc,
  391. exc_info=1)
  392. os._exit(1)
  393. def start(self, *args, **kwargs):
  394. self._was_started = True
  395. super(PoolThread, self).start(*args, **kwargs)
  396. def on_stop_not_started(self):
  397. pass
  398. def stop(self, timeout=None):
  399. if self._was_started:
  400. self.join(timeout)
  401. return
  402. self.on_stop_not_started()
  403. def terminate(self):
  404. self._state = TERMINATE
  405. def close(self):
  406. self._state = CLOSE
  407. class Supervisor(PoolThread):
  408. def __init__(self, pool):
  409. self.pool = pool
  410. super(Supervisor, self).__init__()
  411. def body(self):
  412. debug('worker handler starting')
  413. time.sleep(0.8)
  414. pool = self.pool
  415. try:
  416. # do a burst at startup to verify that we can start
  417. # our pool processes, and in that time we lower
  418. # the max restart frequency.
  419. prev_state = pool.restart_state
  420. pool.restart_state = restart_state(10 * pool._processes, 1)
  421. for _ in range(10):
  422. if self._state == RUN and pool._state == RUN:
  423. pool._maintain_pool()
  424. time.sleep(0.1)
  425. # Keep maintaing workers until the cache gets drained, unless
  426. # the pool is termianted
  427. pool.restart_state = prev_state
  428. while self._state == RUN and pool._state == RUN:
  429. pool._maintain_pool()
  430. time.sleep(0.8)
  431. except RestartFreqExceeded:
  432. pool.close()
  433. pool.join()
  434. raise
  435. debug('worker handler exiting')
  436. class TaskHandler(PoolThread):
  437. def __init__(self, taskqueue, put, outqueue, pool):
  438. self.taskqueue = taskqueue
  439. self.put = put
  440. self.outqueue = outqueue
  441. self.pool = pool
  442. super(TaskHandler, self).__init__()
  443. def body(self):
  444. taskqueue = self.taskqueue
  445. put = self.put
  446. for taskseq, set_length in iter(taskqueue.get, None):
  447. try:
  448. i = -1
  449. for i, task in enumerate(taskseq):
  450. if self._state:
  451. debug('task handler found thread._state != RUN')
  452. break
  453. try:
  454. put(task)
  455. except IOError:
  456. debug('could not put task on queue')
  457. break
  458. else:
  459. if set_length:
  460. debug('doing set_length()')
  461. set_length(i + 1)
  462. continue
  463. break
  464. except Exception as exc:
  465. error('Task Handler ERROR: %r', exc, exc_info=1)
  466. break
  467. else:
  468. debug('task handler got sentinel')
  469. self.tell_others()
  470. def tell_others(self):
  471. outqueue = self.outqueue
  472. put = self.put
  473. pool = self.pool
  474. try:
  475. # tell result handler to finish when cache is empty
  476. debug('task handler sending sentinel to result handler')
  477. outqueue.put(None)
  478. # tell workers there is no more work
  479. debug('task handler sending sentinel to workers')
  480. for p in pool:
  481. put(None)
  482. except IOError:
  483. debug('task handler got IOError when sending sentinels')
  484. debug('task handler exiting')
  485. def on_stop_not_started(self):
  486. self.tell_others()
  487. class TimeoutHandler(PoolThread):
  488. def __init__(self, processes, cache, t_soft, t_hard):
  489. self.processes = processes
  490. self.cache = cache
  491. self.t_soft = t_soft
  492. self.t_hard = t_hard
  493. self._it = None
  494. super(TimeoutHandler, self).__init__()
  495. def _process_by_pid(self, pid):
  496. return next((
  497. (proc, i) for i, proc in enumerate(self.processes)
  498. if proc.pid == pid
  499. ), (None, None))
  500. def on_soft_timeout(self, job):
  501. debug('soft time limit exceeded for %r', job)
  502. process, _index = self._process_by_pid(job._worker_pid)
  503. if not process:
  504. return
  505. # Run timeout callback
  506. job.handle_timeout(soft=True)
  507. try:
  508. _kill(job._worker_pid, SIG_SOFT_TIMEOUT)
  509. except OSError as exc:
  510. if get_errno(exc) != errno.ESRCH:
  511. raise
  512. def on_hard_timeout(self, job):
  513. if job.ready():
  514. return
  515. debug('hard time limit exceeded for %r', job)
  516. # Remove from cache and set return value to an exception
  517. try:
  518. raise TimeLimitExceeded(job._timeout)
  519. except TimeLimitExceeded:
  520. job._set(job._job, (False, ExceptionInfo()))
  521. else: # pragma: no cover
  522. pass
  523. # Remove from _pool
  524. process, _index = self._process_by_pid(job._worker_pid)
  525. # Run timeout callback
  526. job.handle_timeout(soft=False)
  527. if process:
  528. self._trywaitkill(process)
  529. def _trywaitkill(self, worker):
  530. debug('timeout: sending TERM to %s', worker._name)
  531. try:
  532. worker.terminate()
  533. except OSError:
  534. pass
  535. else:
  536. if worker._popen.wait(timeout=0.1):
  537. return
  538. debug('timeout: TERM timed-out, now sending KILL to %s', worker._name)
  539. try:
  540. _kill(worker.pid, SIGKILL)
  541. except OSError:
  542. pass
  543. def handle_timeouts(self):
  544. cache = self.cache
  545. t_hard, t_soft = self.t_hard, self.t_soft
  546. dirty = set()
  547. on_soft_timeout = self.on_soft_timeout
  548. on_hard_timeout = self.on_hard_timeout
  549. def _timed_out(start, timeout):
  550. if not start or not timeout:
  551. return False
  552. if monotonic() >= start + timeout:
  553. return True
  554. # Inner-loop
  555. while self._state == RUN:
  556. # Remove dirty items not in cache anymore
  557. if dirty:
  558. dirty = set(k for k in dirty if k in cache)
  559. for i, job in list(cache.items()):
  560. ack_time = job._time_accepted
  561. soft_timeout = job._soft_timeout
  562. if soft_timeout is None:
  563. soft_timeout = t_soft
  564. hard_timeout = job._timeout
  565. if hard_timeout is None:
  566. hard_timeout = t_hard
  567. if _timed_out(ack_time, hard_timeout):
  568. on_hard_timeout(job)
  569. elif i not in dirty and _timed_out(ack_time, soft_timeout):
  570. on_soft_timeout(job)
  571. dirty.add(i)
  572. yield
  573. def body(self):
  574. while self._state == RUN:
  575. try:
  576. for _ in self.handle_timeouts():
  577. time.sleep(1.0) # don't spin
  578. except CoroStop:
  579. break
  580. debug('timeout handler exiting')
  581. def handle_event(self, *args):
  582. if self._it is None:
  583. self._it = self.handle_timeouts()
  584. try:
  585. next(self._it)
  586. except StopIteration:
  587. self._it = None
  588. class ResultHandler(PoolThread):
  589. def __init__(self, outqueue, get, cache, poll,
  590. join_exited_workers, putlock, restart_state,
  591. check_timeouts, on_job_ready):
  592. self.outqueue = outqueue
  593. self.get = get
  594. self.cache = cache
  595. self.poll = poll
  596. self.join_exited_workers = join_exited_workers
  597. self.putlock = putlock
  598. self.restart_state = restart_state
  599. self._it = None
  600. self._shutdown_complete = False
  601. self.check_timeouts = check_timeouts
  602. self.on_job_ready = on_job_ready
  603. self._make_methods()
  604. super(ResultHandler, self).__init__()
  605. def on_stop_not_started(self):
  606. # used when pool started without result handler thread.
  607. self.finish_at_shutdown(handle_timeouts=True)
  608. def _make_methods(self):
  609. cache = self.cache
  610. putlock = self.putlock
  611. restart_state = self.restart_state
  612. on_job_ready = self.on_job_ready
  613. def on_ack(job, i, time_accepted, pid, synqW_fd):
  614. restart_state.R = 0
  615. try:
  616. cache[job]._ack(i, time_accepted, pid, synqW_fd)
  617. except (KeyError, AttributeError):
  618. # Object gone or doesn't support _ack (e.g. IMAPIterator).
  619. pass
  620. def on_ready(job, i, obj, inqW_fd):
  621. if on_job_ready is not None:
  622. on_job_ready(job, i, obj, inqW_fd)
  623. try:
  624. item = cache[job]
  625. except KeyError:
  626. return
  627. if not item.ready():
  628. if putlock is not None:
  629. putlock.release()
  630. try:
  631. item._set(i, obj)
  632. except KeyError:
  633. pass
  634. def on_death(pid, exitcode):
  635. try:
  636. os.kill(pid, signal.SIGTERM)
  637. except OSError as exc:
  638. if get_errno(exc) != errno.ESRCH:
  639. raise
  640. state_handlers = self.state_handlers = {
  641. ACK: on_ack, READY: on_ready, DEATH: on_death
  642. }
  643. def on_state_change(task):
  644. state, args = task
  645. try:
  646. state_handlers[state](*args)
  647. except KeyError:
  648. debug("Unknown job state: %s (args=%s)", state, args)
  649. self.on_state_change = on_state_change
  650. def _process_result(self, timeout=1.0):
  651. poll = self.poll
  652. on_state_change = self.on_state_change
  653. while 1:
  654. try:
  655. ready, task = poll(timeout)
  656. except (IOError, EOFError) as exc:
  657. debug('result handler got %r -- exiting', exc)
  658. raise CoroStop()
  659. if self._state:
  660. assert self._state == TERMINATE
  661. debug('result handler found thread._state=TERMINATE')
  662. raise CoroStop()
  663. if ready:
  664. if task is None:
  665. debug('result handler got sentinel')
  666. raise CoroStop()
  667. on_state_change(task)
  668. if timeout != 0: # blocking
  669. break
  670. else:
  671. break
  672. yield
  673. def handle_event(self, fileno=None, events=None):
  674. if self._state == RUN:
  675. if self._it is None:
  676. self._it = self._process_result(0) # non-blocking
  677. try:
  678. next(self._it)
  679. except (StopIteration, CoroStop):
  680. self._it = None
  681. def body(self):
  682. debug('result handler starting')
  683. try:
  684. while self._state == RUN:
  685. try:
  686. for _ in self._process_result(1.0): # blocking
  687. pass
  688. except CoroStop:
  689. break
  690. finally:
  691. self.finish_at_shutdown()
  692. def finish_at_shutdown(self, handle_timeouts=False):
  693. self._shutdown_complete = True
  694. get = self.get
  695. outqueue = self.outqueue
  696. cache = self.cache
  697. poll = self.poll
  698. join_exited_workers = self.join_exited_workers
  699. check_timeouts = self.check_timeouts
  700. on_state_change = self.on_state_change
  701. time_terminate = None
  702. while cache and self._state != TERMINATE:
  703. if check_timeouts is not None:
  704. check_timeouts()
  705. try:
  706. ready, task = poll(1.0)
  707. except (IOError, EOFError) as exc:
  708. debug('result handler got %r -- exiting', exc)
  709. return
  710. if ready:
  711. if task is None:
  712. debug('result handler ignoring extra sentinel')
  713. continue
  714. on_state_change(task)
  715. try:
  716. join_exited_workers(shutdown=True)
  717. except WorkersJoined:
  718. now = monotonic()
  719. if not time_terminate:
  720. time_terminate = now
  721. else:
  722. if now - time_terminate > 5.0:
  723. debug('result handler exiting: timed out')
  724. break
  725. debug('result handler: all workers terminated, '
  726. 'timeout in %ss',
  727. abs(min(now - time_terminate - 5.0, 0)))
  728. if hasattr(outqueue, '_reader'):
  729. debug('ensuring that outqueue is not full')
  730. # If we don't make room available in outqueue then
  731. # attempts to add the sentinel (None) to outqueue may
  732. # block. There is guaranteed to be no more than 2 sentinels.
  733. try:
  734. for i in range(10):
  735. if not outqueue._reader.poll():
  736. break
  737. get()
  738. except (IOError, EOFError):
  739. pass
  740. debug('result handler exiting: len(cache)=%s, thread._state=%s',
  741. len(cache), self._state)
  742. class Pool(object):
  743. '''
  744. Class which supports an async version of applying functions to arguments.
  745. '''
  746. Worker = Worker
  747. Supervisor = Supervisor
  748. TaskHandler = TaskHandler
  749. TimeoutHandler = TimeoutHandler
  750. ResultHandler = ResultHandler
  751. SoftTimeLimitExceeded = SoftTimeLimitExceeded
  752. def __init__(self, processes=None, initializer=None, initargs=(),
  753. maxtasksperchild=None, timeout=None, soft_timeout=None,
  754. lost_worker_timeout=None,
  755. max_restarts=None, max_restart_freq=1,
  756. on_process_up=None,
  757. on_process_down=None,
  758. on_timeout_set=None,
  759. on_timeout_cancel=None,
  760. threads=True,
  761. semaphore=None,
  762. putlocks=False,
  763. allow_restart=False,
  764. synack=False,
  765. on_process_exit=None,
  766. **kwargs):
  767. self.synack = synack
  768. self._setup_queues()
  769. self._taskqueue = Queue()
  770. self._cache = {}
  771. self._state = RUN
  772. self.timeout = timeout
  773. self.soft_timeout = soft_timeout
  774. self._maxtasksperchild = maxtasksperchild
  775. self._initializer = initializer
  776. self._initargs = initargs
  777. self._on_process_exit = on_process_exit
  778. self.lost_worker_timeout = lost_worker_timeout or LOST_WORKER_TIMEOUT
  779. self.on_process_up = on_process_up
  780. self.on_process_down = on_process_down
  781. self.on_timeout_set = on_timeout_set
  782. self.on_timeout_cancel = on_timeout_cancel
  783. self.threads = threads
  784. self.readers = {}
  785. self.allow_restart = allow_restart
  786. if soft_timeout and SIG_SOFT_TIMEOUT is None:
  787. warnings.warn(UserWarning(
  788. "Soft timeouts are not supported: "
  789. "on this platform: It does not have the SIGUSR1 signal.",
  790. ))
  791. soft_timeout = None
  792. self._processes = self.cpu_count() if processes is None else processes
  793. self.max_restarts = max_restarts or round(self._processes * 100)
  794. self.restart_state = restart_state(max_restarts, max_restart_freq or 1)
  795. if initializer is not None and not callable(initializer):
  796. raise TypeError('initializer must be a callable')
  797. if on_process_exit is not None and not callable(on_process_exit):
  798. raise TypeError('on_process_exit must be callable')
  799. self._pool = []
  800. self._poolctrl = {}
  801. self.putlocks = putlocks
  802. self._putlock = semaphore or LaxBoundedSemaphore(self._processes)
  803. for i in range(self._processes):
  804. self._create_worker_process(i)
  805. self._worker_handler = self.Supervisor(self)
  806. if threads:
  807. self._worker_handler.start()
  808. self._task_handler = self.TaskHandler(self._taskqueue,
  809. self._quick_put,
  810. self._outqueue,
  811. self._pool)
  812. if threads:
  813. self._task_handler.start()
  814. # Thread killing timedout jobs.
  815. self._timeout_handler = self.TimeoutHandler(
  816. self._pool, self._cache,
  817. self.soft_timeout, self.timeout,
  818. )
  819. self._timeout_handler_mutex = Lock()
  820. self._timeout_handler_started = False
  821. if self.timeout is not None or self.soft_timeout is not None:
  822. self._start_timeout_handler()
  823. # If running without threads, we need to check for timeouts
  824. # while waiting for unfinished work at shutdown.
  825. self.check_timeouts = None
  826. if not threads:
  827. self.check_timeouts = self._timeout_handler.handle_event
  828. # Thread processing results in the outqueue.
  829. self._result_handler = self.create_result_handler()
  830. self.handle_result_event = self._result_handler.handle_event
  831. if threads:
  832. self._result_handler.start()
  833. self._terminate = Finalize(
  834. self, self._terminate_pool,
  835. args=(self._taskqueue, self._inqueue, self._outqueue,
  836. self._pool, self._worker_handler, self._task_handler,
  837. self._result_handler, self._cache,
  838. self._timeout_handler,
  839. self._help_stuff_finish_args()),
  840. exitpriority=15,
  841. )
  842. def create_result_handler(self, **extra_kwargs):
  843. return self.ResultHandler(
  844. self._outqueue, self._quick_get, self._cache,
  845. self._poll_result, self._join_exited_workers,
  846. self._putlock, self.restart_state, self.check_timeouts,
  847. self.on_job_ready, **extra_kwargs
  848. )
  849. def on_job_ready(self, job, i, obj, inqW_fd):
  850. pass
  851. def _help_stuff_finish_args(self):
  852. return self._inqueue, self._task_handler, self._pool
  853. def cpu_count(self):
  854. try:
  855. return cpu_count()
  856. except NotImplementedError:
  857. return 1
  858. def handle_result_event(self, *args):
  859. return self._result_handler.handle_event(*args)
  860. def _process_register_queues(self, worker, queues):
  861. pass
  862. def _process_by_pid(self, pid):
  863. return next((
  864. (proc, i) for i, proc in enumerate(self._pool)
  865. if proc.pid == pid
  866. ), (None, None))
  867. def get_process_queues(self):
  868. return self._inqueue, self._outqueue, None
  869. def _create_worker_process(self, i):
  870. sentinel = Event() if self.allow_restart else None
  871. inq, outq, synq = self.get_process_queues()
  872. w = self.Worker(
  873. inq, outq, synq, self._initializer, self._initargs,
  874. self._maxtasksperchild, sentinel, self._on_process_exit,
  875. # Need to handle all signals if using the ipc semaphore,
  876. # to make sure the semaphore is released.
  877. sigprotection=self.threads,
  878. )
  879. self._pool.append(w)
  880. self._process_register_queues(w, (inq, outq, synq))
  881. w.name = w.name.replace('Process', 'PoolWorker')
  882. w.daemon = True
  883. w.index = i
  884. w.start()
  885. self._poolctrl[w.pid] = sentinel
  886. if self.on_process_up:
  887. self.on_process_up(w)
  888. return w
  889. def process_flush_queues(self, worker):
  890. pass
  891. def _join_exited_workers(self, shutdown=False):
  892. """Cleanup after any worker processes which have exited due to
  893. reaching their specified lifetime. Returns True if any workers were
  894. cleaned up.
  895. """
  896. now = None
  897. # The worker may have published a result before being terminated,
  898. # but we have no way to accurately tell if it did. So we wait for
  899. # _lost_worker_timeout seconds before we mark the job with
  900. # WorkerLostError.
  901. for job in [job for job in list(self._cache.values())
  902. if not job.ready() and job._worker_lost]:
  903. now = now or monotonic()
  904. lost_time, lost_ret = job._worker_lost
  905. if now - lost_time > job._lost_worker_timeout:
  906. self.mark_as_worker_lost(job, lost_ret)
  907. if shutdown and not len(self._pool):
  908. raise WorkersJoined()
  909. cleaned, exitcodes = {}, {}
  910. for i in reversed(range(len(self._pool))):
  911. worker = self._pool[i]
  912. exitcode = worker.exitcode
  913. popen = worker._popen
  914. if popen is None or exitcode is not None:
  915. # worker exited
  916. debug('Supervisor: cleaning up worker %d', i)
  917. if popen is not None:
  918. worker.join()
  919. debug('Supervisor: worked %d joined', i)
  920. cleaned[worker.pid] = worker
  921. exitcodes[worker.pid] = exitcode
  922. if exitcode not in (EX_OK, EX_RECYCLE) and \
  923. not getattr(worker, '_controlled_termination', False):
  924. error(
  925. 'Process %r pid:%r exited with %r',
  926. worker.name, worker.pid, human_status(exitcode),
  927. exc_info=0,
  928. )
  929. self.process_flush_queues(worker)
  930. del self._pool[i]
  931. del self._poolctrl[worker.pid]
  932. if cleaned:
  933. all_pids = [w.pid for w in self._pool]
  934. for job in list(self._cache.values()):
  935. acked_by_gone = next(
  936. (pid for pid in job.worker_pids()
  937. if pid in cleaned or pid not in all_pids),
  938. None
  939. )
  940. # already accepted by process
  941. if acked_by_gone:
  942. self.on_job_process_down(job, acked_by_gone)
  943. if not job.ready():
  944. exitcode = exitcodes.get(acked_by_gone) or 0
  945. proc = cleaned.get(acked_by_gone)
  946. if proc and getattr(proc, '_job_terminated', False):
  947. job._set_terminated(exitcode)
  948. else:
  949. self.on_job_process_lost(
  950. job, acked_by_gone, exitcode,
  951. )
  952. else:
  953. # started writing to
  954. write_to = job._write_to
  955. # was scheduled to write to
  956. sched_for = job._scheduled_for
  957. if write_to and not write_to._is_alive():
  958. self.on_job_process_down(job, write_to.pid)
  959. elif sched_for and not sched_for._is_alive():
  960. self.on_job_process_down(job, sched_for.pid)
  961. for worker in values(cleaned):
  962. if self.on_process_down:
  963. if not shutdown:
  964. self._process_cleanup_queues(worker)
  965. self.on_process_down(worker)
  966. return list(exitcodes.values())
  967. return []
  968. def on_partial_read(self, job, worker):
  969. pass
  970. def _process_cleanup_queues(self, worker):
  971. pass
  972. def on_job_process_down(self, job, pid_gone):
  973. pass
  974. def on_job_process_lost(self, job, pid, exitcode):
  975. job._worker_lost = (monotonic(), exitcode)
  976. def mark_as_worker_lost(self, job, exitcode):
  977. try:
  978. raise WorkerLostError(
  979. 'Worker exited prematurely: {0}.'.format(
  980. human_status(exitcode)),
  981. )
  982. except WorkerLostError:
  983. job._set(None, (False, ExceptionInfo()))
  984. else: # pragma: no cover
  985. pass
  986. def __enter__(self):
  987. return self
  988. def __exit__(self, *exc_info):
  989. return self.terminate()
  990. def on_grow(self, n):
  991. pass
  992. def on_shrink(self, n):
  993. pass
  994. def shrink(self, n=1):
  995. for i, worker in enumerate(self._iterinactive()):
  996. self._processes -= 1
  997. if self._putlock:
  998. self._putlock.shrink()
  999. worker.terminate_controlled()
  1000. self.on_shrink(1)
  1001. if i >= n - 1:
  1002. break
  1003. else:
  1004. raise ValueError("Can't shrink pool. All processes busy!")
  1005. def grow(self, n=1):
  1006. for i in range(n):
  1007. self._processes += 1
  1008. if self._putlock:
  1009. self._putlock.grow()
  1010. self.on_grow(n)
  1011. def _iterinactive(self):
  1012. for worker in self._pool:
  1013. if not self._worker_active(worker):
  1014. yield worker
  1015. raise StopIteration()
  1016. def _worker_active(self, worker):
  1017. for job in values(self._cache):
  1018. if worker.pid in job.worker_pids():
  1019. return True
  1020. return False
  1021. def _repopulate_pool(self, exitcodes):
  1022. """Bring the number of pool processes up to the specified number,
  1023. for use after reaping workers which have exited.
  1024. """
  1025. for i in range(self._processes - len(self._pool)):
  1026. if self._state != RUN:
  1027. return
  1028. try:
  1029. if exitcodes and exitcodes[i] not in (EX_OK, EX_RECYCLE):
  1030. self.restart_state.step()
  1031. except IndexError:
  1032. self.restart_state.step()
  1033. self._create_worker_process(self._avail_index())
  1034. debug('added worker')
  1035. def _avail_index(self):
  1036. assert len(self._pool) < self._processes
  1037. indices = set(p.index for p in self._pool)
  1038. return next(i for i in range(self._processes) if i not in indices)
  1039. def did_start_ok(self):
  1040. return not self._join_exited_workers()
  1041. def _maintain_pool(self):
  1042. """"Clean up any exited workers and start replacements for them.
  1043. """
  1044. joined = self._join_exited_workers()
  1045. self._repopulate_pool(joined)
  1046. for i in range(len(joined)):
  1047. if self._putlock is not None:
  1048. self._putlock.release()
  1049. def maintain_pool(self):
  1050. if self._worker_handler._state == RUN and self._state == RUN:
  1051. try:
  1052. self._maintain_pool()
  1053. except RestartFreqExceeded:
  1054. self.close()
  1055. self.join()
  1056. raise
  1057. except OSError as exc:
  1058. if get_errno(exc) == errno.ENOMEM:
  1059. reraise(MemoryError,
  1060. MemoryError(str(exc)),
  1061. sys.exc_info()[2])
  1062. raise
  1063. def _setup_queues(self):
  1064. from billiard.queues import SimpleQueue
  1065. self._inqueue = SimpleQueue()
  1066. self._outqueue = SimpleQueue()
  1067. self._quick_put = self._inqueue._writer.send
  1068. self._quick_get = self._outqueue._reader.recv
  1069. def _poll_result(timeout):
  1070. if self._outqueue._reader.poll(timeout):
  1071. return True, self._quick_get()
  1072. return False, None
  1073. self._poll_result = _poll_result
  1074. def _start_timeout_handler(self):
  1075. # ensure more than one thread does not start the timeout handler
  1076. # thread at once.
  1077. if self.threads:
  1078. with self._timeout_handler_mutex:
  1079. if not self._timeout_handler_started:
  1080. self._timeout_handler_started = True
  1081. self._timeout_handler.start()
  1082. def apply(self, func, args=(), kwds={}):
  1083. '''
  1084. Equivalent of `func(*args, **kwargs)`.
  1085. '''
  1086. if self._state == RUN:
  1087. return self.apply_async(func, args, kwds).get()
  1088. def starmap(self, func, iterable, chunksize=None):
  1089. '''
  1090. Like `map()` method but the elements of the `iterable` are expected to
  1091. be iterables as well and will be unpacked as arguments. Hence
  1092. `func` and (a, b) becomes func(a, b).
  1093. '''
  1094. if self._state == RUN:
  1095. return self._map_async(func, iterable,
  1096. starmapstar, chunksize).get()
  1097. def starmap_async(self, func, iterable, chunksize=None,
  1098. callback=None, error_callback=None):
  1099. '''
  1100. Asynchronous version of `starmap()` method.
  1101. '''
  1102. if self._state == RUN:
  1103. return self._map_async(func, iterable, starmapstar, chunksize,
  1104. callback, error_callback)
  1105. def map(self, func, iterable, chunksize=None):
  1106. '''
  1107. Apply `func` to each element in `iterable`, collecting the results
  1108. in a list that is returned.
  1109. '''
  1110. if self._state == RUN:
  1111. return self.map_async(func, iterable, chunksize).get()
  1112. def imap(self, func, iterable, chunksize=1, lost_worker_timeout=None):
  1113. '''
  1114. Equivalent of `map()` -- can be MUCH slower than `Pool.map()`.
  1115. '''
  1116. if self._state != RUN:
  1117. return
  1118. lost_worker_timeout = lost_worker_timeout or self.lost_worker_timeout
  1119. if chunksize == 1:
  1120. result = IMapIterator(self._cache,
  1121. lost_worker_timeout=lost_worker_timeout)
  1122. self._taskqueue.put((
  1123. ((TASK, (result._job, i, func, (x,), {}))
  1124. for i, x in enumerate(iterable)),
  1125. result._set_length,
  1126. ))
  1127. return result
  1128. else:
  1129. assert chunksize > 1
  1130. task_batches = Pool._get_tasks(func, iterable, chunksize)
  1131. result = IMapIterator(self._cache,
  1132. lost_worker_timeout=lost_worker_timeout)
  1133. self._taskqueue.put((
  1134. ((TASK, (result._job, i, mapstar, (x,), {}))
  1135. for i, x in enumerate(task_batches)),
  1136. result._set_length,
  1137. ))
  1138. return (item for chunk in result for item in chunk)
  1139. def imap_unordered(self, func, iterable, chunksize=1,
  1140. lost_worker_timeout=None):
  1141. '''
  1142. Like `imap()` method but ordering of results is arbitrary.
  1143. '''
  1144. if self._state != RUN:
  1145. return
  1146. lost_worker_timeout = lost_worker_timeout or self.lost_worker_timeout
  1147. if chunksize == 1:
  1148. result = IMapUnorderedIterator(
  1149. self._cache, lost_worker_timeout=lost_worker_timeout,
  1150. )
  1151. self._taskqueue.put((
  1152. ((TASK, (result._job, i, func, (x,), {}))
  1153. for i, x in enumerate(iterable)),
  1154. result._set_length,
  1155. ))
  1156. return result
  1157. else:
  1158. assert chunksize > 1
  1159. task_batches = Pool._get_tasks(func, iterable, chunksize)
  1160. result = IMapUnorderedIterator(
  1161. self._cache, lost_worker_timeout=lost_worker_timeout,
  1162. )
  1163. self._taskqueue.put((
  1164. ((TASK, (result._job, i, mapstar, (x,), {}))
  1165. for i, x in enumerate(task_batches)),
  1166. result._set_length,
  1167. ))
  1168. return (item for chunk in result for item in chunk)
  1169. def apply_async(self, func, args=(), kwds={},
  1170. callback=None, error_callback=None, accept_callback=None,
  1171. timeout_callback=None, waitforslot=None,
  1172. soft_timeout=None, timeout=None, lost_worker_timeout=None,
  1173. callbacks_propagate=(),
  1174. correlation_id=None):
  1175. '''
  1176. Asynchronous equivalent of `apply()` method.
  1177. Callback is called when the functions return value is ready.
  1178. The accept callback is called when the job is accepted to be executed.
  1179. Simplified the flow is like this:
  1180. >>> def apply_async(func, args, kwds, callback, accept_callback):
  1181. ... if accept_callback:
  1182. ... accept_callback()
  1183. ... retval = func(*args, **kwds)
  1184. ... if callback:
  1185. ... callback(retval)
  1186. '''
  1187. if self._state != RUN:
  1188. return
  1189. soft_timeout = soft_timeout or self.soft_timeout
  1190. timeout = timeout or self.timeout
  1191. lost_worker_timeout = lost_worker_timeout or self.lost_worker_timeout
  1192. if soft_timeout and SIG_SOFT_TIMEOUT is None:
  1193. warnings.warn(UserWarning(
  1194. "Soft timeouts are not supported: "
  1195. "on this platform: It does not have the SIGUSR1 signal.",
  1196. ))
  1197. soft_timeout = None
  1198. if self._state == RUN:
  1199. waitforslot = self.putlocks if waitforslot is None else waitforslot
  1200. if waitforslot and self._putlock is not None:
  1201. self._putlock.acquire()
  1202. result = ApplyResult(
  1203. self._cache, callback, accept_callback, timeout_callback,
  1204. error_callback, soft_timeout, timeout, lost_worker_timeout,
  1205. on_timeout_set=self.on_timeout_set,
  1206. on_timeout_cancel=self.on_timeout_cancel,
  1207. callbacks_propagate=callbacks_propagate,
  1208. send_ack=self.send_ack if self.synack else None,
  1209. correlation_id=correlation_id,
  1210. )
  1211. if timeout or soft_timeout:
  1212. # start the timeout handler thread when required.
  1213. self._start_timeout_handler()
  1214. if self.threads:
  1215. self._taskqueue.put(([(TASK, (result._job, None,
  1216. func, args, kwds))], None))
  1217. else:
  1218. self._quick_put((TASK, (result._job, None, func, args, kwds)))
  1219. return result
  1220. def send_ack(self, response, job, i, fd):
  1221. pass
  1222. def terminate_job(self, pid, sig=None):
  1223. proc, _ = self._process_by_pid(pid)
  1224. if proc is not None:
  1225. try:
  1226. _kill(pid, sig or signal.SIGTERM)
  1227. except OSError as exc:
  1228. if get_errno(exc) != errno.ESRCH:
  1229. raise
  1230. else:
  1231. proc._controlled_termination = True
  1232. proc._job_terminated = True
  1233. def map_async(self, func, iterable, chunksize=None,
  1234. callback=None, error_callback=None):
  1235. '''
  1236. Asynchronous equivalent of `map()` method.
  1237. '''
  1238. return self._map_async(
  1239. func, iterable, mapstar, chunksize, callback, error_callback,
  1240. )
  1241. def _map_async(self, func, iterable, mapper, chunksize=None,
  1242. callback=None, error_callback=None):
  1243. '''
  1244. Helper function to implement map, starmap and their async counterparts.
  1245. '''
  1246. if self._state != RUN:
  1247. return
  1248. if not hasattr(iterable, '__len__'):
  1249. iterable = list(iterable)
  1250. if chunksize is None:
  1251. chunksize, extra = divmod(len(iterable), len(self._pool) * 4)
  1252. if extra:
  1253. chunksize += 1
  1254. if len(iterable) == 0:
  1255. chunksize = 0
  1256. task_batches = Pool._get_tasks(func, iterable, chunksize)
  1257. result = MapResult(self._cache, chunksize, len(iterable), callback,
  1258. error_callback=error_callback)
  1259. self._taskqueue.put((((TASK, (result._job, i, mapper, (x,), {}))
  1260. for i, x in enumerate(task_batches)), None))
  1261. return result
  1262. @staticmethod
  1263. def _get_tasks(func, it, size):
  1264. it = iter(it)
  1265. while 1:
  1266. x = tuple(itertools.islice(it, size))
  1267. if not x:
  1268. return
  1269. yield (func, x)
  1270. def __reduce__(self):
  1271. raise NotImplementedError(
  1272. 'pool objects cannot be passed between processes or pickled',
  1273. )
  1274. def close(self):
  1275. debug('closing pool')
  1276. if self._state == RUN:
  1277. self._state = CLOSE
  1278. if self._putlock:
  1279. self._putlock.clear()
  1280. self._worker_handler.close()
  1281. self._taskqueue.put(None)
  1282. stop_if_not_current(self._worker_handler)
  1283. def terminate(self):
  1284. debug('terminating pool')
  1285. self._state = TERMINATE
  1286. self._worker_handler.terminate()
  1287. self._terminate()
  1288. @staticmethod
  1289. def _stop_task_handler(task_handler):
  1290. stop_if_not_current(task_handler)
  1291. def join(self):
  1292. assert self._state in (CLOSE, TERMINATE)
  1293. debug('joining worker handler')
  1294. stop_if_not_current(self._worker_handler)
  1295. debug('joining task handler')
  1296. self._stop_task_handler(self._task_handler)
  1297. debug('joining result handler')
  1298. stop_if_not_current(self._result_handler)
  1299. debug('result handler joined')
  1300. for i, p in enumerate(self._pool):
  1301. debug('joining worker %s/%s (%r)', i+1, len(self._pool), p)
  1302. if p._popen is not None: # process started?
  1303. p.join()
  1304. debug('pool join complete')
  1305. def restart(self):
  1306. for e in values(self._poolctrl):
  1307. e.set()
  1308. @staticmethod
  1309. def _help_stuff_finish(inqueue, task_handler, _pool):
  1310. # task_handler may be blocked trying to put items on inqueue
  1311. debug('removing tasks from inqueue until task handler finished')
  1312. inqueue._rlock.acquire()
  1313. while task_handler.is_alive() and inqueue._reader.poll():
  1314. inqueue._reader.recv()
  1315. time.sleep(0)
  1316. @classmethod
  1317. def _set_result_sentinel(cls, outqueue, pool):
  1318. outqueue.put(None)
  1319. @classmethod
  1320. def _terminate_pool(cls, taskqueue, inqueue, outqueue, pool,
  1321. worker_handler, task_handler,
  1322. result_handler, cache, timeout_handler,
  1323. help_stuff_finish_args):
  1324. # this is guaranteed to only be called once
  1325. debug('finalizing pool')
  1326. worker_handler.terminate()
  1327. task_handler.terminate()
  1328. taskqueue.put(None) # sentinel
  1329. debug('helping task handler/workers to finish')
  1330. cls._help_stuff_finish(*help_stuff_finish_args)
  1331. result_handler.terminate()
  1332. cls._set_result_sentinel(outqueue, pool)
  1333. if timeout_handler is not None:
  1334. timeout_handler.terminate()
  1335. # Terminate workers which haven't already finished
  1336. if pool and hasattr(pool[0], 'terminate'):
  1337. debug('terminating workers')
  1338. for p in pool:
  1339. if p._is_alive():
  1340. p.terminate()
  1341. debug('joining task handler')
  1342. cls._stop_task_handler(task_handler)
  1343. debug('joining result handler')
  1344. result_handler.stop()
  1345. if timeout_handler is not None:
  1346. debug('joining timeout handler')
  1347. timeout_handler.stop(TIMEOUT_MAX)
  1348. if pool and hasattr(pool[0], 'terminate'):
  1349. debug('joining pool workers')
  1350. for p in pool:
  1351. if p.is_alive():
  1352. # worker has not yet exited
  1353. debug('cleaning up worker %d', p.pid)
  1354. if p._popen is not None:
  1355. p.join()
  1356. debug('pool workers joined')
  1357. @property
  1358. def process_sentinels(self):
  1359. return [w._popen.sentinel for w in self._pool]
  1360. #
  1361. # Class whose instances are returned by `Pool.apply_async()`
  1362. #
  1363. class ApplyResult(object):
  1364. _worker_lost = None
  1365. _write_to = None
  1366. _scheduled_for = None
  1367. def __init__(self, cache, callback, accept_callback=None,
  1368. timeout_callback=None, error_callback=None, soft_timeout=None,
  1369. timeout=None, lost_worker_timeout=LOST_WORKER_TIMEOUT,
  1370. on_timeout_set=None, on_timeout_cancel=None,
  1371. callbacks_propagate=(), send_ack=None,
  1372. correlation_id=None):
  1373. self.correlation_id = correlation_id
  1374. self._mutex = Lock()
  1375. self._event = threading.Event()
  1376. self._job = next(job_counter)
  1377. self._cache = cache
  1378. self._callback = callback
  1379. self._accept_callback = accept_callback
  1380. self._error_callback = error_callback
  1381. self._timeout_callback = timeout_callback
  1382. self._timeout = timeout
  1383. self._soft_timeout = soft_timeout
  1384. self._lost_worker_timeout = lost_worker_timeout
  1385. self._on_timeout_set = on_timeout_set
  1386. self._on_timeout_cancel = on_timeout_cancel
  1387. self._callbacks_propagate = callbacks_propagate or ()
  1388. self._send_ack = send_ack
  1389. self._accepted = False
  1390. self._cancelled = False
  1391. self._worker_pid = None
  1392. self._time_accepted = None
  1393. self._terminated = None
  1394. cache[self._job] = self
  1395. def __repr__(self):
  1396. return '<Result: {id} ack:{ack} ready:{ready}>'.format(
  1397. id=self._job, ack=self._accepted, ready=self.ready(),
  1398. )
  1399. def ready(self):
  1400. return self._event.isSet()
  1401. def accepted(self):
  1402. return self._accepted
  1403. def successful(self):
  1404. assert self.ready()
  1405. return self._success
  1406. def _cancel(self):
  1407. """Only works if synack is used."""
  1408. self._cancelled = True
  1409. def discard(self):
  1410. self._cache.pop(self._job, None)
  1411. def terminate(self, signum):
  1412. self._terminated = signum
  1413. def _set_terminated(self, signum=None):
  1414. try:
  1415. raise Terminated(-(signum or 0))
  1416. except Terminated:
  1417. self._set(None, (False, ExceptionInfo()))
  1418. def worker_pids(self):
  1419. return [self._worker_pid] if self._worker_pid else []
  1420. def wait(self, timeout=None):
  1421. self._event.wait(timeout)
  1422. def get(self, timeout=None):
  1423. self.wait(timeout)
  1424. if not self.ready():
  1425. raise TimeoutError
  1426. if self._success:
  1427. return self._value
  1428. else:
  1429. raise self._value.exception
  1430. def safe_apply_callback(self, fun, *args, **kwargs):
  1431. if fun:
  1432. try:
  1433. fun(*args, **kwargs)
  1434. except self._callbacks_propagate:
  1435. raise
  1436. except Exception as exc:
  1437. error('Pool callback raised exception: %r', exc,
  1438. exc_info=1)
  1439. def handle_timeout(self, soft=False):
  1440. if self._timeout_callback is not None:
  1441. self.safe_apply_callback(
  1442. self._timeout_callback, soft=soft,
  1443. timeout=self._soft_timeout if soft else self._timeout,
  1444. )
  1445. def _set(self, i, obj):
  1446. with self._mutex:
  1447. if self._on_timeout_cancel:
  1448. self._on_timeout_cancel(self)
  1449. self._success, self._value = obj
  1450. self._event.set()
  1451. if self._accepted:
  1452. # if not accepted yet, then the set message
  1453. # was received before the ack, which means
  1454. # the ack will remove the entry.
  1455. self._cache.pop(self._job, None)
  1456. # apply callbacks last
  1457. if self._callback and self._success:
  1458. self.safe_apply_callback(
  1459. self._callback, self._value)
  1460. if (self._value is not None and
  1461. self._error_callback and not self._success):
  1462. self.safe_apply_callback(
  1463. self._error_callback, self._value)
  1464. def _ack(self, i, time_accepted, pid, synqW_fd):
  1465. with self._mutex:
  1466. if self._cancelled and self._send_ack:
  1467. self._accepted = True
  1468. if synqW_fd:
  1469. return self._send_ack(NACK, pid, self._job, synqW_fd)
  1470. return
  1471. self._accepted = True
  1472. self._time_accepted = time_accepted
  1473. self._worker_pid = pid
  1474. if self.ready():
  1475. # ack received after set()
  1476. self._cache.pop(self._job, None)
  1477. if self._on_timeout_set:
  1478. self._on_timeout_set(self, self._soft_timeout, self._timeout)
  1479. response = ACK
  1480. if self._accept_callback:
  1481. try:
  1482. self._accept_callback(pid, time_accepted)
  1483. except self._propagate_errors:
  1484. response = NACK
  1485. raise
  1486. except Exception:
  1487. response = NACK
  1488. # ignore other errors
  1489. finally:
  1490. if self._send_ack and synqW_fd:
  1491. return self._send_ack(
  1492. response, pid, self._job, synqW_fd
  1493. )
  1494. if self._send_ack and synqW_fd:
  1495. self._send_ack(response, pid, self._job, synqW_fd)
  1496. #
  1497. # Class whose instances are returned by `Pool.map_async()`
  1498. #
  1499. class MapResult(ApplyResult):
  1500. def __init__(self, cache, chunksize, length, callback, error_callback):
  1501. ApplyResult.__init__(
  1502. self, cache, callback, error_callback=error_callback,
  1503. )
  1504. self._success = True
  1505. self._length = length
  1506. self._value = [None] * length
  1507. self._accepted = [False] * length
  1508. self._worker_pid = [None] * length
  1509. self._time_accepted = [None] * length
  1510. self._chunksize = chunksize
  1511. if chunksize <= 0:
  1512. self._number_left = 0
  1513. self._event.set()
  1514. del cache[self._job]
  1515. else:
  1516. self._number_left = length // chunksize + bool(length % chunksize)
  1517. def _set(self, i, success_result):
  1518. success, result = success_result
  1519. if success:
  1520. self._value[i * self._chunksize:(i + 1) * self._chunksize] = result
  1521. self._number_left -= 1
  1522. if self._number_left == 0:
  1523. if self._callback:
  1524. self._callback(self._value)
  1525. if self._accepted:
  1526. self._cache.pop(self._job, None)
  1527. self._event.set()
  1528. else:
  1529. self._success = False
  1530. self._value = result
  1531. if self._error_callback:
  1532. self._error_callback(self._value)
  1533. if self._accepted:
  1534. self._cache.pop(self._job, None)
  1535. self._event.set()
  1536. def _ack(self, i, time_accepted, pid, *args):
  1537. start = i * self._chunksize
  1538. stop = min((i + 1) * self._chunksize, self._length)
  1539. for j in range(start, stop):
  1540. self._accepted[j] = True
  1541. self._worker_pid[j] = pid
  1542. self._time_accepted[j] = time_accepted
  1543. if self.ready():
  1544. self._cache.pop(self._job, None)
  1545. def accepted(self):
  1546. return all(self._accepted)
  1547. def worker_pids(self):
  1548. return [pid for pid in self._worker_pid if pid]
  1549. #
  1550. # Class whose instances are returned by `Pool.imap()`
  1551. #
  1552. class IMapIterator(object):
  1553. _worker_lost = None
  1554. def __init__(self, cache, lost_worker_timeout=LOST_WORKER_TIMEOUT):
  1555. self._cond = threading.Condition(threading.Lock())
  1556. self._job = next(job_counter)
  1557. self._cache = cache
  1558. self._items = deque()
  1559. self._index = 0
  1560. self._length = None
  1561. self._ready = False
  1562. self._unsorted = {}
  1563. self._worker_pids = []
  1564. self._lost_worker_timeout = lost_worker_timeout
  1565. cache[self._job] = self
  1566. def __iter__(self):
  1567. return self
  1568. def next(self, timeout=None):
  1569. with self._cond:
  1570. try:
  1571. item = self._items.popleft()
  1572. except IndexError:
  1573. if self._index == self._length:
  1574. self._ready = True
  1575. raise StopIteration
  1576. self._cond.wait(timeout)
  1577. try:
  1578. item = self._items.popleft()
  1579. except IndexError:
  1580. if self._index == self._length:
  1581. self._ready = True
  1582. raise StopIteration
  1583. raise TimeoutError
  1584. success, value = item
  1585. if success:
  1586. return value
  1587. raise Exception(value)
  1588. __next__ = next # XXX
  1589. def _set(self, i, obj):
  1590. with self._cond:
  1591. if self._index == i:
  1592. self._items.append(obj)
  1593. self._index += 1
  1594. while self._index in self._unsorted:
  1595. obj = self._unsorted.pop(self._index)
  1596. self._items.append(obj)
  1597. self._index += 1
  1598. self._cond.notify()
  1599. else:
  1600. self._unsorted[i] = obj
  1601. if self._index == self._length:
  1602. self._ready = True
  1603. del self._cache[self._job]
  1604. def _set_length(self, length):
  1605. with self._cond:
  1606. self._length = length
  1607. if self._index == self._length:
  1608. self._ready = True
  1609. self._cond.notify()
  1610. del self._cache[self._job]
  1611. def _ack(self, i, time_accepted, pid, *args):
  1612. self._worker_pids.append(pid)
  1613. def ready(self):
  1614. return self._ready
  1615. def worker_pids(self):
  1616. return self._worker_pids
  1617. #
  1618. # Class whose instances are returned by `Pool.imap_unordered()`
  1619. #
  1620. class IMapUnorderedIterator(IMapIterator):
  1621. def _set(self, i, obj):
  1622. with self._cond:
  1623. self._items.append(obj)
  1624. self._index += 1
  1625. self._cond.notify()
  1626. if self._index == self._length:
  1627. self._ready = True
  1628. del self._cache[self._job]
  1629. #
  1630. #
  1631. #
  1632. class ThreadPool(Pool):
  1633. from billiard.dummy import Process as DummyProcess
  1634. Process = DummyProcess
  1635. def __init__(self, processes=None, initializer=None, initargs=()):
  1636. Pool.__init__(self, processes, initializer, initargs)
  1637. def _setup_queues(self):
  1638. self._inqueue = Queue()
  1639. self._outqueue = Queue()
  1640. self._quick_put = self._inqueue.put
  1641. self._quick_get = self._outqueue.get
  1642. def _poll_result(timeout):
  1643. try:
  1644. return True, self._quick_get(timeout=timeout)
  1645. except Empty:
  1646. return False, None
  1647. self._poll_result = _poll_result
  1648. @staticmethod
  1649. def _help_stuff_finish(inqueue, task_handler, pool):
  1650. # put sentinels at head of inqueue to make workers finish
  1651. with inqueue.not_empty:
  1652. inqueue.queue.clear()
  1653. inqueue.queue.extend([None] * len(pool))
  1654. inqueue.not_empty.notify_all()