summaryrefslogtreecommitdiff
path: root/src/mailman/runners/rest.py
diff options
context:
space:
mode:
authorBarry Warsaw2013-06-17 09:36:43 -0400
committerBarry Warsaw2013-06-17 09:36:43 -0400
commit41059ed20ec668baf41cceaf539f8017171e9651 (patch)
tree391b33289ecb1d02905a897ed581ac9538531f10 /src/mailman/runners/rest.py
parentbffd71903475efad53ce6aa59c96a704a905a984 (diff)
downloadmailman-41059ed20ec668baf41cceaf539f8017171e9651.tar.gz
mailman-41059ed20ec668baf41cceaf539f8017171e9651.tar.zst
mailman-41059ed20ec668baf41cceaf539f8017171e9651.zip
Diffstat (limited to 'src/mailman/runners/rest.py')
-rw-r--r--src/mailman/runners/rest.py64
1 files changed, 42 insertions, 22 deletions
diff --git a/src/mailman/runners/rest.py b/src/mailman/runners/rest.py
index 252e41ef0..67987805d 100644
--- a/src/mailman/runners/rest.py
+++ b/src/mailman/runners/rest.py
@@ -25,11 +25,9 @@ __all__ = [
]
-import sys
-import errno
-import select
import signal
import logging
+import threading
from mailman.core.runner import Runner
from mailman.rest.wsgiapp import make_server
@@ -40,25 +38,47 @@ log = logging.getLogger('mailman.http')
class RESTRunner(Runner):
- #intercept_signals = False
+ # Don't install the standard signal handlers because as defined, they
+ # won't actually stop the TCPServer started by .serve_forever().
is_queue_runner = False
+ def __init__(self, name, slice=None):
+ """See `IRunner`."""
+ super(RESTRunner, self).__init__(name, slice)
+ # Both the REST server and the signal handlers must run in the main
+ # thread; the former because of SQLite requirements (objects created
+ # in one thread cannot be shared with the other threads), and the
+ # latter because of Python's signal handling semantics.
+ #
+ # Unfortunately, we cannot issue a TCPServer shutdown in the main
+ # thread, because that will cause a deadlock. Yay. So what we do is
+ # to use the signal handler to notify a shutdown thread that the
+ # shutdown should happen. That thread will wake up and stop the main
+ # server.
+ self._server = make_server()
+ self._event = threading.Event()
+ def stopper(event, server):
+ event.wait()
+ server.shutdown()
+ self._thread = threading.Thread(
+ target=stopper, args=(self._event, self._server))
+ self._thread.start()
+
def run(self):
- log.info('Starting REST server')
- # Handle SIGTERM the same way as SIGINT.
- def stop_server(signum, frame):
- log.info('REST server shutdown')
- sys.exit(signal.SIGTERM)
- signal.signal(signal.SIGTERM, stop_server)
- try:
- make_server().serve_forever()
- except KeyboardInterrupt:
- log.info('REST server interrupted')
- sys.exit(signal.SIGINT)
- except select.error as (errcode, message):
- if errcode == errno.EINTR:
- log.info('REST server exiting')
- sys.exit(errno.EINTR)
- raise
- except:
- raise
+ """See `IRunner`."""
+ self._server.serve_forever()
+
+ def signal_handler(self, signum, frame):
+ super(RESTRunner, self).signal_handler(signum, frame)
+ if signum in (signal.SIGTERM, signal.SIGINT, signal.SIGUSR1):
+ # Set the flag that will terminate the TCPserver loop.
+ self._event.set()
+
+ def _one_iteration(self):
+ # Just keep going
+ if self._thread.is_alive():
+ self._thread.join(timeout=0.1)
+ return 1
+
+ def _snooze(self, filecnt):
+ pass