[FIX] core: allow signal handlers in multiprocess workers

Because Python only runs signal handlers on the main thread, native
calls (IO or accept(2)) can delay these handlers running. This is
especially problematic when one such call is completely stuck and
we're trying to dump the stack to diagnose the issue.

By running the actual worker's processing in a sub-thread and leaving
the main thread sleeping, worker processes should always be able to
handle signals.

closes odoo/odoo#30688
This commit is contained in:
Xavier Morel
2019-01-30 11:22:05 +00:00
parent 1dbe4ea0d7
commit 6ce2d6efb5
+19 -6
View File
@@ -685,7 +685,7 @@ class Worker(object):
if e.args[0] not in [errno.EINTR]:
raise
def process_limit(self):
def check_limits(self):
# If our parent changed sucide
if self.ppid != os.getppid():
_logger.info("Worker (%s) Parent changed", self.pid)
@@ -704,6 +704,7 @@ class Worker(object):
soft, hard = resource.getrlimit(resource.RLIMIT_AS)
resource.setrlimit(resource.RLIMIT_AS, (config['limit_memory_hard'], hard))
def set_limits(self):
# SIGXCPU (exceeded CPU time) signal handler will raise an exception.
r = resource.getrusage(resource.RUSAGE_SELF)
cpu_time = r.ru_utime + r.ru_stime
@@ -736,17 +737,18 @@ class Worker(object):
signal.signal(signal.SIGCHLD, signal.SIG_DFL)
signal.set_wakeup_fd(self.wakeup_fd_w)
self.set_limits()
def stop(self):
pass
def run(self):
try:
self.start()
while self.alive:
self.process_limit()
self.multi.pipe_ping(self.watchdog_pipe)
self.sleep()
self.process_work()
t = threading.Thread(name="Worker %s (%s) workthread" % (self.__class__.__name__, self.pid), target=self._runloop)
t.daemon = True
t.start()
t.join()
_logger.info("Worker (%s) exiting. request_count: %s, registry count: %s.",
self.pid, self.request_count,
len(odoo.modules.registry.Registry.registries))
@@ -756,6 +758,17 @@ class Worker(object):
# should we use 3 to abort everything ?
sys.exit(1)
def _runloop(self):
try:
while self.alive:
self.multi.pipe_ping(self.watchdog_pipe)
self.sleep()
self.process_work()
self.check_limits()
except:
_logger.exception("Worker %s (%s) Exception occured, exiting...", self.__class__.__name__, self.pid)
sys.exit(1)
class WorkerHTTP(Worker):
""" HTTP Request workers """
def process_request(self, client, addr):