From 6ce2d6efb5b53451f369d544a31c36a024e0faf5 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 30 Jan 2019 10:00:37 +0000 Subject: [PATCH] [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 --- odoo/service/server.py | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/odoo/service/server.py b/odoo/service/server.py index 8ca6fa0e981..60392d25b10 100644 --- a/odoo/service/server.py +++ b/odoo/service/server.py @@ -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):