diff --git a/nova/openstack/common/service.py b/nova/openstack/common/service.py index 58aaa79d4bac..ea94f9e2dd83 100644 --- a/nova/openstack/common/service.py +++ b/nova/openstack/common/service.py @@ -206,12 +206,16 @@ class ProcessLauncher(object): for handler in cls._signal_handlers_set: handler(*args, **kwargs) - def __init__(self): - """Constructor.""" + def __init__(self, wait_interval=0.01): + """Constructor. + :param wait_interval: The interval to sleep for between checks + of child process exit. + """ self.children = {} self.sigcaught = None self.running = True + self.wait_interval = wait_interval rfd, self.writepipe = os.pipe() self.readpipe = eventlet.greenio.GreenPipe(rfd, 'r') self.handle_signal() @@ -238,15 +242,12 @@ class ProcessLauncher(object): def _child_process_handle_signal(self): # Setup child signal handlers differently - def _sigterm(*args): - signal.signal(signal.SIGTERM, signal.SIG_DFL) - raise SignalExit(signal.SIGTERM) - def _sighup(*args): signal.signal(signal.SIGHUP, signal.SIG_DFL) raise SignalExit(signal.SIGHUP) - signal.signal(signal.SIGTERM, _sigterm) + # Parent signals with SIGTERM when it wants us to go away. + signal.signal(signal.SIGTERM, signal.SIG_DFL) if _sighup_supported(): signal.signal(signal.SIGHUP, _sighup) # Block SIGINT and let the parent send us a SIGTERM @@ -337,8 +338,8 @@ class ProcessLauncher(object): def _wait_child(self): try: - # Block while any of child processes have exited - pid, status = os.waitpid(0, 0) + # Don't block if no child processes have exited + pid, status = os.waitpid(0, os.WNOHANG) if not pid: return None except OSError as exc: @@ -367,6 +368,10 @@ class ProcessLauncher(object): while self.running: wrap = self._wait_child() if not wrap: + # Yield to other threads if no children have exited + # Sleep for a short time to avoid excessive CPU usage + # (see bug #1095346) + eventlet.greenthread.sleep(self.wait_interval) continue while self.running and len(wrap.children) < wrap.workers: self._start_child(wrap)