- bug fix in killed process detection
This commit is contained in:
@@ -7,6 +7,7 @@ from pickle import PicklingError, dispatch_table
|
|||||||
from psutil import Process
|
from psutil import Process
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
from warnings import warn
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from javabridge import kill_vm
|
from javabridge import kill_vm
|
||||||
@@ -423,6 +424,7 @@ class parpool(object):
|
|||||||
ctx = multiprocessing.get_context('spawn')
|
ctx = multiprocessing.get_context('spawn')
|
||||||
else:
|
else:
|
||||||
ctx = multiprocessing
|
ctx = multiprocessing
|
||||||
|
self.is_started = False
|
||||||
self.n_tasks = ctx.Value('i', self.nP)
|
self.n_tasks = ctx.Value('i', self.nP)
|
||||||
self.event = ctx.Event()
|
self.event = ctx.Event()
|
||||||
self.queue_in = ctx.Queue(qsize or 3 * self.nP)
|
self.queue_in = ctx.Queue(qsize or 3 * self.nP)
|
||||||
@@ -453,6 +455,7 @@ class parpool(object):
|
|||||||
for handle, task in self.tasks.items(): # retry a task if the process doing it was killed
|
for handle, task in self.tasks.items(): # retry a task if the process doing it was killed
|
||||||
if task.pid is not None and task.pid not in [child.pid for child in Process().children()]:
|
if task.pid is not None and task.pid not in [child.pid for child in Process().children()]:
|
||||||
self.queue_in.put(task)
|
self.queue_in.put(task)
|
||||||
|
warn('Task {} was restarted because process {} was probably killed.'.format(task.handle, task.pid))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def error(self, error):
|
def error(self, error):
|
||||||
@@ -460,23 +463,27 @@ class parpool(object):
|
|||||||
raise Exception('Error occured in worker: {}'.format(error))
|
raise Exception('Error occured in worker: {}'.format(error))
|
||||||
|
|
||||||
def task_error(self, handle, error):
|
def task_error(self, handle, error):
|
||||||
task = self.tasks[handle]
|
if handle in self:
|
||||||
print('Error from process working on iteration {}:\n'.format(handle))
|
task = self.tasks[handle]
|
||||||
print(error)
|
print('Error from process working on iteration {}:\n'.format(handle))
|
||||||
self.close()
|
print(error)
|
||||||
print('Retrying in main thread...')
|
self.close()
|
||||||
fun = task.fun.__name__
|
print('Retrying in main thread...')
|
||||||
task()
|
fun = task.fun.__name__
|
||||||
raise Exception('Function \'{}\' cannot be executed by parfor, amend or execute in serial.'.format(fun))
|
task()
|
||||||
|
raise Exception('Function \'{}\' cannot be executed by parfor, amend or execute in serial.'.format(fun))
|
||||||
|
|
||||||
def done(self, task):
|
def done(self, task):
|
||||||
self.tasks[task.handle] = task
|
if task.handle in self: # if not, the task was restarted erroneously
|
||||||
if self.bar is not None:
|
self.tasks[task.handle] = task
|
||||||
self.bar.update(self.bar_lengths.pop(task.handle))
|
if self.bar is not None:
|
||||||
self._qbar_update()
|
self.bar.update(self.bar_lengths.pop(task.handle))
|
||||||
|
self._qbar_update()
|
||||||
|
|
||||||
def started(self, handle, pid):
|
def started(self, handle, pid):
|
||||||
self.tasks[handle].pid = pid
|
self.is_started = True
|
||||||
|
if handle in self: # if not, the task was restarted erroneously
|
||||||
|
self.tasks[handle].pid = pid
|
||||||
|
|
||||||
def __call__(self, n, fun=None, args=None, kwargs=None, handle=None, barlength=1):
|
def __call__(self, n, fun=None, args=None, kwargs=None, handle=None, barlength=1):
|
||||||
""" Add new iteration, using optional manually defined handle."""
|
""" Add new iteration, using optional manually defined handle."""
|
||||||
@@ -514,11 +521,13 @@ class parpool(object):
|
|||||||
if handle not in self:
|
if handle not in self:
|
||||||
raise ValueError('No handle: {}'.format(handle))
|
raise ValueError('No handle: {}'.format(handle))
|
||||||
while not self.tasks[handle].done:
|
while not self.tasks[handle].done:
|
||||||
if not self._get_from_queue() and not self.tasks[handle].done and not self.working:
|
if not self._get_from_queue() and not self.tasks[handle].done and self.is_started and not self.working:
|
||||||
for _ in range(10): # wait some time while processing possible new messages
|
for _ in range(10): # wait some time while processing possible new messages
|
||||||
self._get_from_queue()
|
self._get_from_queue()
|
||||||
if not self._get_from_queue() and not self.tasks[handle].done and not self.working:
|
if not self._get_from_queue() and not self.tasks[handle].done and self.is_started and not self.working:
|
||||||
|
# retry a task if the process was killed while retrieving the task
|
||||||
self.queue_in.put(self.tasks[handle])
|
self.queue_in.put(self.tasks[handle])
|
||||||
|
warn('Task {} was restarted because the process retrieving it was probably killed.'.format(handle))
|
||||||
result = self.tasks[handle].result
|
result = self.tasks[handle].result
|
||||||
self.tasks.pop(handle)
|
self.tasks.pop(handle)
|
||||||
return result
|
return result
|
||||||
@@ -607,8 +616,8 @@ class parpool(object):
|
|||||||
try:
|
try:
|
||||||
task = self.queue_in.get(True, 0.02)
|
task = self.queue_in.get(True, 0.02)
|
||||||
try:
|
try:
|
||||||
task.set_from_cache(self.cache)
|
|
||||||
self.add_to_queue('started', task.handle, pid)
|
self.add_to_queue('started', task.handle, pid)
|
||||||
|
task.set_from_cache(self.cache)
|
||||||
self.add_to_queue('done', task())
|
self.add_to_queue('done', task())
|
||||||
except Exception:
|
except Exception:
|
||||||
self.add_to_queue('task_error', task.handle, format_exc())
|
self.add_to_queue('task_error', task.handle, format_exc())
|
||||||
|
|||||||
2
setup.py
2
setup.py
@@ -5,7 +5,7 @@ with open("README.md", "r") as fh:
|
|||||||
|
|
||||||
setuptools.setup(
|
setuptools.setup(
|
||||||
name="parfor",
|
name="parfor",
|
||||||
version="2022.3.0",
|
version="2022.3.1",
|
||||||
author="Wim Pomp",
|
author="Wim Pomp",
|
||||||
author_email="wimpomp@gmail.com",
|
author_email="wimpomp@gmail.com",
|
||||||
description="A package to mimic the use of parfor as done in Matlab.",
|
description="A package to mimic the use of parfor as done in Matlab.",
|
||||||
|
|||||||
Reference in New Issue
Block a user