Add support for cmdline reloading when requests are blocked.

This commit is contained in:
Griatch 2017-06-04 00:35:55 +02:00
parent 00f71667ba
commit d73a2876cf
2 changed files with 27 additions and 23 deletions

View file

@ -16,7 +16,7 @@ import os
from twisted.web import static from twisted.web import static
from twisted.application import internet, service from twisted.application import internet, service
from twisted.internet import reactor, defer from twisted.internet import reactor, defer
from twisted.internet.task import LoopingCall from twisted.internet.task import LoopingCall, deferLater
import django import django
django.setup() django.setup()
@ -168,10 +168,18 @@ class Evennia(object):
# initialize channelhandler # initialize channelhandler
channelhandler.CHANNELHANDLER.update() channelhandler.CHANNELHANDLER.update()
# set a callback if the server is killed abruptly, # wrap the SIGINT handler to make sure we empty the threadpool
# by Ctrl-C, reboot etc. # even when we reload and we have long-running requests in queue.
reactor.addSystemEventTrigger('before', 'shutdown', # this is necessary over using Twisted's signal handler.
self.shutdown, _reactor_stopping=True) # (see https://github.com/evennia/evennia/issues/1128)
def _wrap_sigint_handler(*args):
from twisted.internet.defer import Deferred
d = self.web_root.empty_threadpool()
d.addCallback(lambda _: self.shutdown(_reactor_stopping=True))
d.addCallback(lambda _: reactor.stop())
reactor.callLater(1, d.callback, None)
reactor.sigInt = _wrap_sigint_handler
self.game_running = True self.game_running = True
# track the server time # track the server time
@ -349,9 +357,6 @@ class Evennia(object):
from evennia.server.models import ServerConfig from evennia.server.models import ServerConfig
from evennia.utils import gametime as _GAMETIME_MODULE from evennia.utils import gametime as _GAMETIME_MODULE
# lock the threadpool from accepting more requests
self.web_root.pool.lock()
if mode == 'reload': if mode == 'reload':
# call restart hooks # call restart hooks
ServerConfig.objects.conf("server_restart_mode", "reload") ServerConfig.objects.conf("server_restart_mode", "reload")
@ -387,24 +392,17 @@ class Evennia(object):
# always called, also for a reload # always called, also for a reload
self.at_server_stop() self.at_server_stop()
# if _reactor_stopping is true, reactor does not need to
# be stopped again.
if os.name == 'nt' and os.path.exists(SERVER_PIDFILE): if os.name == 'nt' and os.path.exists(SERVER_PIDFILE):
# for Windows we need to remove pid files manually # for Windows we need to remove pid files manually
os.remove(SERVER_PIDFILE) os.remove(SERVER_PIDFILE)
if WEBSERVER_ENABLED:
yield self.web_root.empty_threadpool()
if not _reactor_stopping: if not _reactor_stopping:
# this will also send a reactor.stop signal, so we set a # kill the server
# flag to avoid loops.
self.shutdown_complete = True self.shutdown_complete = True
if WEBSERVER_ENABLED: reactor.callLater(1, reactor.stop)
# Make sure to not continue until threadpool queue is empty.
deferred = self.web_root.get_pending_requests()
deferred.addCallback(lambda _: reactor.stop())
from twisted.internet import task
yield task.deferLater(reactor, 1, deferred.callback, None)
else:
# kill the server
reactor.callLater(1, reactor.stop)
# we make sure the proper gametime is saved as late as possible # we make sure the proper gametime is saved as late as possible
ServerConfig.objects.conf("runtime", _GAMETIME_MODULE.runtime()) ServerConfig.objects.conf("runtime", _GAMETIME_MODULE.runtime())

View file

@ -149,12 +149,13 @@ class DjangoWebRoot(resource.Resource):
pool (ThreadPool): The twisted threadpool. pool (ThreadPool): The twisted threadpool.
""" """
self._pool = pool self.pool = pool
self._echo_log = True
self._pending_requests = {} self._pending_requests = {}
resource.Resource.__init__(self) resource.Resource.__init__(self)
self.wsgi_resource = WSGIResource(reactor, pool, WSGIHandler()) self.wsgi_resource = WSGIResource(reactor, pool, WSGIHandler())
def get_pending_requests(self): def empty_threadpool(self):
""" """
Converts our _pending_requests list of deferreds into a DeferredList Converts our _pending_requests list of deferreds into a DeferredList
@ -162,6 +163,11 @@ class DjangoWebRoot(resource.Resource):
deflist (DeferredList): Contains all deferreds of pending requests. deflist (DeferredList): Contains all deferreds of pending requests.
""" """
self.pool.lock()
if self._pending_requests and self._echo_log:
self._echo_log = False # just to avoid multiple echoes
msg = "Webserver waiting for %i requests ... "
logger.log_info(msg % len(self._pending_requests))
return defer.DeferredList(self._pending_requests, consumeErrors=True) return defer.DeferredList(self._pending_requests, consumeErrors=True)
def _decrement_requests(self, *args, **kwargs): def _decrement_requests(self, *args, **kwargs):