|
@@ -15,6 +15,11 @@ from functools import partial as curry
|
|
|
|
|
|
|
|
|
|
def pid_is_dead(pid):
|
|
def pid_is_dead(pid):
|
|
|
|
+ """Check if a process is not running by PID.
|
|
|
|
+
|
|
|
|
+ :rtype bool:
|
|
|
|
+
|
|
|
|
+ """
|
|
try:
|
|
try:
|
|
return os.kill(pid, 0)
|
|
return os.kill(pid, 0)
|
|
except OSError, err:
|
|
except OSError, err:
|
|
@@ -27,6 +32,12 @@ def pid_is_dead(pid):
|
|
|
|
|
|
|
|
|
|
def reap_process(pid):
|
|
def reap_process(pid):
|
|
|
|
+ """Reap process if the process is a zombie.
|
|
|
|
+
|
|
|
|
+ :returns: ``True`` if process was reaped or is not running,
|
|
|
|
+ ``False`` otherwise.
|
|
|
|
+
|
|
|
|
+ """
|
|
if pid_is_dead(pid):
|
|
if pid_is_dead(pid):
|
|
return True
|
|
return True
|
|
|
|
|
|
@@ -40,6 +51,20 @@ def reap_process(pid):
|
|
|
|
|
|
|
|
|
|
def process_is_dead(process):
|
|
def process_is_dead(process):
|
|
|
|
+ """Check if process is not running anymore.
|
|
|
|
+
|
|
|
|
+ First it finds out if the process is running by sending
|
|
|
|
+ signal 0. Then if the process is a child process, and is running
|
|
|
|
+ it finds out if it's a zombie process and reaps it.
|
|
|
|
+ If the process is running and is not a zombie it tries to send
|
|
|
|
+ a ping through the process pipe.
|
|
|
|
+
|
|
|
|
+ :param process: A :class:`multiprocessing.Process` instance.
|
|
|
|
+
|
|
|
|
+ :returns: ``True`` if the process is not running, ``False`` otherwise.
|
|
|
|
+
|
|
|
|
+ """
|
|
|
|
+
|
|
# Make sure PID is an integer (no idea why this happens).
|
|
# Make sure PID is an integer (no idea why this happens).
|
|
try:
|
|
try:
|
|
int(process.pid)
|
|
int(process.pid)
|
|
@@ -92,29 +117,42 @@ class DynamicPool(Pool):
|
|
w.start()
|
|
w.start()
|
|
|
|
|
|
def grow(self, size=1):
|
|
def grow(self, size=1):
|
|
- """Add ``size`` new workers to the pool."""
|
|
|
|
|
|
+ """Add workers to the pool.
|
|
|
|
+
|
|
|
|
+ :keyword size: Number of workers to add (default: 1)
|
|
|
|
+
|
|
|
|
+ """
|
|
[self.add_worker() for i in range(size)]
|
|
[self.add_worker() for i in range(size)]
|
|
|
|
|
|
- def is_dead(self, process):
|
|
|
|
|
|
+ def _is_dead(self, process):
|
|
|
|
+ """Try to find out if the process is dead.
|
|
|
|
+
|
|
|
|
+ :rtype bool:
|
|
|
|
+
|
|
|
|
+ """
|
|
if process_is_dead(process):
|
|
if process_is_dead(process):
|
|
self.logger.info("DynamicPool: Found dead process (PID: %s)" % (
|
|
self.logger.info("DynamicPool: Found dead process (PID: %s)" % (
|
|
process.pid))
|
|
process.pid))
|
|
return True
|
|
return True
|
|
return False
|
|
return False
|
|
|
|
|
|
- def bring_out_the_dead(self):
|
|
|
|
- dead = []
|
|
|
|
- alive = []
|
|
|
|
|
|
+ def _bring_out_the_dead(self):
|
|
|
|
+ """Sort out dead process from pool.
|
|
|
|
+
|
|
|
|
+ :returns: Tuple of two lists, the first list with dead processes,
|
|
|
|
+ the second with active processes.
|
|
|
|
+
|
|
|
|
+ """
|
|
|
|
+ dead, alive = [], []
|
|
for process in self._pool:
|
|
for process in self._pool:
|
|
if process and process.pid:
|
|
if process and process.pid:
|
|
- if self.is_dead(process):
|
|
|
|
- dead += [process]
|
|
|
|
- else:
|
|
|
|
- alive += [process]
|
|
|
|
|
|
+ dest = dead if self._is_dead(process) else alive
|
|
|
|
+ dest.append(process)
|
|
return dead, alive
|
|
return dead, alive
|
|
|
|
|
|
def replace_dead_workers(self):
|
|
def replace_dead_workers(self):
|
|
- dead, self._pool = self.bring_out_the_dead()
|
|
|
|
|
|
+ """Replace dead workers in the pool by spawning new ones."""
|
|
|
|
+ dead, self._pool = self._bring_out_the_dead()
|
|
self.grow(self._size if len(dead) > self._size else len(dead))
|
|
self.grow(self._size if len(dead) > self._size else len(dead))
|
|
|
|
|
|
|
|
|