|
@@ -75,17 +75,19 @@ class LaxBoundedSemaphore(threading._Semaphore):
|
|
|
|
|
|
def release(self):
|
|
|
if self._Semaphore__value < self._initial_value:
|
|
|
- return _Semaphore.release(self)
|
|
|
+ _Semaphore.release(self)
|
|
|
if __debug__:
|
|
|
self._note("%s.release: success, value=%s (unchanged)" % (
|
|
|
self, self._Semaphore__value))
|
|
|
|
|
|
+ def clear(self):
|
|
|
+ while self._Semaphore__value < self._initial_value:
|
|
|
+ _Semaphore.release(self)
|
|
|
|
|
|
#
|
|
|
-# Code run by worker processes
|
|
|
+# Exceptions
|
|
|
#
|
|
|
|
|
|
-
|
|
|
class MaybeEncodingError(Exception):
|
|
|
"""Wraps unpickleable object."""
|
|
|
|
|
@@ -102,9 +104,18 @@ class MaybeEncodingError(Exception):
|
|
|
self.value, self.exc)
|
|
|
|
|
|
|
|
|
+class WorkersJoined(Exception):
|
|
|
+ """All workers have terminated."""
|
|
|
+
|
|
|
+
|
|
|
def soft_timeout_sighandler(signum, frame):
|
|
|
raise SoftTimeLimitExceeded()
|
|
|
|
|
|
+#
|
|
|
+# Code run by worker processes
|
|
|
+#
|
|
|
+
|
|
|
+
|
|
|
|
|
|
def worker(inqueue, outqueue, initializer=None, initargs=(), maxtasks=None):
|
|
|
pid = os.getpid()
|
|
@@ -410,10 +421,7 @@ class ResultHandler(PoolThread):
|
|
|
|
|
|
on_state_change(task)
|
|
|
|
|
|
- # Notify waiting threads
|
|
|
- if putlock is not None:
|
|
|
- putlock.release()
|
|
|
-
|
|
|
+ time_terminate = None
|
|
|
while cache and self._state != TERMINATE:
|
|
|
try:
|
|
|
ready, task = poll(0.2)
|
|
@@ -427,7 +435,19 @@ class ResultHandler(PoolThread):
|
|
|
continue
|
|
|
|
|
|
on_state_change(task)
|
|
|
- join_exited_workers()
|
|
|
+ try:
|
|
|
+ join_exited_workers(shutdown=True)
|
|
|
+ except WorkersJoined:
|
|
|
+ now = time.time()
|
|
|
+ if not time_terminate:
|
|
|
+ time_terminate = now
|
|
|
+ else:
|
|
|
+ if now - time_terminate > 5.0:
|
|
|
+ debug('result handler exiting: timed out')
|
|
|
+ break
|
|
|
+ debug('result handler: all workers terminated, '
|
|
|
+ 'timeout in %ss' % (
|
|
|
+ abs(min(now - time_terminate - 5.0, 0))))
|
|
|
|
|
|
if hasattr(outqueue, '_reader'):
|
|
|
debug('ensuring that outqueue is not full')
|
|
@@ -536,7 +556,7 @@ class Pool(object):
|
|
|
w.start()
|
|
|
return w
|
|
|
|
|
|
- def _join_exited_workers(self, lost_worker_timeout=10.0):
|
|
|
+ def _join_exited_workers(self, shutdown=False, lost_worker_timeout=10.0):
|
|
|
"""Cleanup after any worker processes which have exited due to
|
|
|
reaching their specified lifetime. Returns True if any workers were
|
|
|
cleaned up.
|
|
@@ -552,13 +572,17 @@ class Pool(object):
|
|
|
err = WorkerLostError("Worker exited prematurely.")
|
|
|
job._set(None, (False, err))
|
|
|
|
|
|
+ if shutdown and not len(self._pool):
|
|
|
+ raise WorkersJoined()
|
|
|
+
|
|
|
cleaned = []
|
|
|
for i in reversed(range(len(self._pool))):
|
|
|
worker = self._pool[i]
|
|
|
if worker.exitcode is not None:
|
|
|
# worker exited
|
|
|
- debug('cleaning up worker %d' % i)
|
|
|
+ debug('Supervisor: cleaning up worker %d' % i)
|
|
|
worker.join()
|
|
|
+ debug('Supervisor: worked %d joined' % i)
|
|
|
cleaned.append(worker.pid)
|
|
|
del self._pool[i]
|
|
|
if cleaned:
|
|
@@ -711,8 +735,10 @@ class Pool(object):
|
|
|
result = ApplyResult(self._cache, callback,
|
|
|
accept_callback, timeout_callback,
|
|
|
error_callback)
|
|
|
- if waitforslot:
|
|
|
+ if waitforslot and self._putlock is not None:
|
|
|
self._putlock.acquire()
|
|
|
+ if self._state != RUN:
|
|
|
+ return
|
|
|
self._taskqueue.put(([(result._job, None, func, args, kwds)], None))
|
|
|
return result
|
|
|
|
|
@@ -758,6 +784,8 @@ class Pool(object):
|
|
|
self._worker_handler.close()
|
|
|
self._worker_handler.join()
|
|
|
self._taskqueue.put(None)
|
|
|
+ if self._putlock:
|
|
|
+ self._putlock.clear()
|
|
|
|
|
|
def terminate(self):
|
|
|
debug('terminating pool')
|
|
@@ -773,6 +801,7 @@ class Pool(object):
|
|
|
self._task_handler.join()
|
|
|
debug('joining result handler')
|
|
|
self._result_handler.join()
|
|
|
+ debug('result handler joined')
|
|
|
for i, p in enumerate(self._pool):
|
|
|
debug('joining worker %s/%s (%r)' % (i, len(self._pool), p, ))
|
|
|
p.join()
|
|
@@ -802,8 +831,6 @@ class Pool(object):
|
|
|
debug('helping task handler/workers to finish')
|
|
|
cls._help_stuff_finish(inqueue, task_handler, len(pool))
|
|
|
|
|
|
- assert result_handler.is_alive() or len(cache) == 0
|
|
|
-
|
|
|
result_handler.terminate()
|
|
|
outqueue.put(None) # sentinel
|
|
|
|
|
@@ -834,6 +861,7 @@ class Pool(object):
|
|
|
# worker has not yet exited
|
|
|
debug('cleaning up worker %d' % p.pid)
|
|
|
p.join()
|
|
|
+ debug('pool workers joined')
|
|
|
DynamicPool = Pool
|
|
|
|
|
|
#
|