Skip to content

Commit

Permalink
arbiter: clean up main loop
Browse files Browse the repository at this point in the history
* Look up handlers in __init__() to induce run-time error early on if
  something is wrong.

* Since we now know that all handlers exist, we can simplify the main
  loop in arbiter, in such a way that we don't need to call wakeup().

So after this commit, the pipe in arbiter is only used to deliver
which signal was sent.
  • Loading branch information
sylt committed Feb 4, 2024
1 parent d77aa6d commit e912247
Showing 1 changed file with 17 additions and 35 deletions.
52 changes: 17 additions & 35 deletions gunicorn/arbiter.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,9 @@ class Arbiter(object):
WORKERS = {}
PIPE = []

# Indicator used to signal that the arbiter main thread has work to do
WAKE_UP = 0

# I love dynamic languages
SIGNALS = [getattr(signal.Signals, "SIG%s" % x)
for x in "CHLD HUP QUIT INT TERM TTIN TTOU USR1 USR2 WINCH".split()]
SIG_NAMES = dict((sig, sig.name[3:].lower()) for sig in SIGNALS)

def __init__(self, app):
os.environ["SERVER_SOFTWARE"] = SERVER_SOFTWARE
Expand Down Expand Up @@ -75,6 +71,11 @@ def __init__(self, app):
0: sys.executable
}

self.SIG_HANDLERS = dict(
(sig, getattr(self, "handle_%s" % sig.name[3:].lower()))
for sig in self.SIGNALS
)

def _get_num_workers(self):
return self._num_workers

Expand Down Expand Up @@ -186,18 +187,14 @@ def init_signals(self):
for s in self.SIGNALS:
signal.signal(s, self.signal)

def write_message_to_pipe(self, one_byte_message):
def signal(self, sig, frame):
""" Note: Signal handler! No logging allowed. """
try:
os.write(self.PIPE[1],
one_byte_message.to_bytes(length=1, byteorder='big'))
os.write(self.PIPE[1], sig.to_bytes(length=1, byteorder='big'))
except IOError as e:
if e.errno not in [errno.EAGAIN, errno.EINTR]:
raise

def signal(self, sig, frame):
""" Note: Signal handler! No logging allowed. """
self.write_message_to_pipe(sig)

# Some UNIXes require SIGCHLD to be reinstalled, see python signal docs
if sig == signal.SIGCHLD:
signal.signal(sig, self.signal)
Expand All @@ -213,25 +210,16 @@ def run(self):
while True:
self.maybe_promote_master()

event_or_signal = self.wait_for_event(timeout_s=1.0)
if event_or_signal is None or event_or_signal == self.WAKE_UP:
self.murder_workers()
self.manage_workers()
continue

sig = event_or_signal
if sig not in self.SIG_NAMES:
self.log.info("Ignoring unknown signal: %s", sig)
continue
sig = self.wait_for_event(timeout_s=1.0)
if sig:
if sig not in self.SIG_HANDLERS:
self.log.info("Ignoring unknown signal: %s", sig)
continue

signame = self.SIG_NAMES.get(sig)
handler = getattr(self, "handle_%s" % signame, None)
if not handler:
self.log.error("Unhandled signal: %s", signame)
continue
self.log.info("Handling signal: %s", signame)
handler()
self.wakeup()
self.log.info("Handling signal: %s", signal.Signals(sig).name)
self.SIG_HANDLERS[sig]()
self.murder_workers()
self.manage_workers()
except (StopIteration, KeyboardInterrupt):
self.halt()
except HaltServer as inst:
Expand Down Expand Up @@ -335,12 +323,6 @@ def maybe_promote_master(self):
# reset proctitle
util._setproctitle("master [%s]" % self.proc_name)

def wakeup(self):
"""\
Wake up the arbiter by writing to the PIPE
"""
self.write_message_to_pipe(self.WAKE_UP)

def halt(self, reason=None, exit_status=0):
""" halt arbiter """
self.stop()
Expand Down

0 comments on commit e912247

Please sign in to comment.