mirror of
https://github.com/frappe/gunicorn.git
synced 2026-01-14 11:09:11 +08:00
Works on sync and eventlet works. Doesn't work on gevent_pywsig or gevent_wsgi workers as we don't control their main loops. Tornado workers appear to be broken. Worst of all, this causes vanilla gevent workers to segfault. I'm waiting to see if there's a known issue before considering what to do next. Worst case we could refuse to run with the bad combination of settings.
123 lines
3.5 KiB
Python
123 lines
3.5 KiB
Python
# -*- coding: utf-8 -
|
|
#
|
|
# This file is part of gunicorn released under the MIT license.
|
|
# See the NOTICE for more information.
|
|
|
|
|
|
import logging
|
|
import os
|
|
import random
|
|
import signal
|
|
import sys
|
|
import tempfile
|
|
|
|
from gunicorn import util
|
|
|
|
class Worker(object):
|
|
|
|
SIGNALS = map(
|
|
lambda x: getattr(signal, "SIG%s" % x),
|
|
"HUP QUIT INT TERM USR1 USR2 WINCH CHLD".split()
|
|
)
|
|
|
|
PIPE = []
|
|
|
|
def __init__(self, age, ppid, socket, app, timeout, cfg):
|
|
"""\
|
|
This is called pre-fork so it shouldn't do anything to the
|
|
current process. If there's a need to make process wide
|
|
changes you'll want to do that in ``self.init_process()``.
|
|
"""
|
|
self.age = age
|
|
self.ppid = ppid
|
|
self.socket = socket
|
|
self.app = app
|
|
self.timeout = timeout
|
|
self.cfg = cfg
|
|
self.booted = False
|
|
|
|
self.nr = 0
|
|
self.max_requests = cfg.max_requests or sys.maxint
|
|
self.alive = True
|
|
self.spinner = 0
|
|
self.log = logging.getLogger(__name__)
|
|
self.debug = cfg.debug
|
|
self.address = self.socket.getsockname()
|
|
|
|
self.fd, self.tmpname = tempfile.mkstemp(prefix="wgunicorn-")
|
|
util.chown(self.tmpname, cfg.uid, cfg.gid)
|
|
self.tmp = os.fdopen(self.fd, "r+b")
|
|
|
|
def __str__(self):
|
|
return "<Worker %s>" % self.pid
|
|
|
|
@property
|
|
def pid(self):
|
|
return os.getpid()
|
|
|
|
def notify(self):
|
|
"""\
|
|
Your worker subclass must arrange to have this method called
|
|
once every ``self.timeout`` seconds. If you fail in accomplishing
|
|
this task, the master process will murder your workers.
|
|
"""
|
|
self.spinner = (self.spinner+1) % 2
|
|
if getattr(os, 'fchmod', None):
|
|
os.fchmod(self.tmp.fileno(), self.spinner)
|
|
else:
|
|
os.chmod(self.tmpname, self.spinner)
|
|
|
|
def run(self):
|
|
"""\
|
|
This is the mainloop of a worker process. You should override
|
|
this method in a subclass to provide the intended behaviour
|
|
for your particular evil schemes.
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
def init_process(self):
|
|
"""\
|
|
If you override this method in a subclass, the last statement
|
|
in the function should be to call this method with
|
|
super(MyWorkerClass, self).init_process() so that the ``run()``
|
|
loop is initiated.
|
|
"""
|
|
util.set_owner_process(self.cfg.uid, self.cfg.gid)
|
|
|
|
# Reseed the random number generator
|
|
random.seed()
|
|
|
|
# For waking ourselves up
|
|
self.PIPE = os.pipe()
|
|
map(util.set_non_blocking, self.PIPE)
|
|
map(util.close_on_exec, self.PIPE)
|
|
|
|
# Prevent fd inherientence
|
|
util.close_on_exec(self.socket)
|
|
util.close_on_exec(self.fd)
|
|
self.init_signals()
|
|
|
|
self.wsgi = self.app.wsgi()
|
|
|
|
# Enter main run loop
|
|
self.booted = True
|
|
self.run()
|
|
|
|
def init_signals(self):
|
|
map(lambda s: signal.signal(s, signal.SIG_DFL), self.SIGNALS)
|
|
signal.signal(signal.SIGQUIT, self.handle_quit)
|
|
signal.signal(signal.SIGTERM, self.handle_exit)
|
|
signal.signal(signal.SIGINT, self.handle_exit)
|
|
signal.signal(signal.SIGWINCH, self.handle_winch)
|
|
|
|
def handle_quit(self, sig, frame):
|
|
self.alive = False
|
|
|
|
def handle_exit(self, sig, frame):
|
|
self.alive = False
|
|
sys.exit(0)
|
|
|
|
def handle_winch(self, sig, fname):
|
|
# Ignore SIGWINCH in worker. Fixes a crash on OpenBSD.
|
|
return
|