~sambuddhabasu1/mailman/fix_mailman_run_error

« back to all changes in this revision

Viewing changes to src/mailman/runners/rest.py

  • Committer: Barry Warsaw
  • Date: 2013-06-17 13:36:43 UTC
  • Revision ID: barry@list.org-20130617133643-uj7atdykh2whwabw
 * `bin/runner` command has been simplified and its command line options
   reduced.  Now, only one `-r/--runner` option may be provided and the
   round-robin feature has been removed.
 * Fixed REST server crash on `reopen` command.  Identification and test
   provided by Aurélien Bompard.  (LP: #1184376)

Also:

 * bin/runner now uses standard argparse instead of ScriptOptions.
 * The entire bin/runner machinery has bee reorganized and simplified.  There
 * is no more Loop class.  Signal setting is moved directly into the base
   Runner class and overrided in specific subclasses (e.g. RESTRunner which
   must cleanly shutdown its TCPServer).  The runner exit status is now set
   directly on the Runner instance.
 * Fixed a few minor style issues.
 * In order to cleanly shutdown the RESTRunner's WSGI server, we must start a
   subthread which only watches for an Event and then calls the server's
   shutdown() method.  It has to be this way because the WSGI server itself
   (due to interactions with SQLite), and the signal handlers (due to Python's
   signal handling semantics) must both run in the main thread.  However, the
   shutdown() must be invoked from a subthread in order to prevent deadlock.
 * Refactor the RESTLayer to eliminate duplication of code.

Show diffs side-by-side

added added

removed removed

Lines of Context:
25
25
    ]
26
26
 
27
27
 
28
 
import sys
29
 
import errno
30
 
import select
31
28
import signal
32
29
import logging
 
30
import threading
33
31
 
34
32
from mailman.core.runner import Runner
35
33
from mailman.rest.wsgiapp import make_server
40
38
 
41
39
 
42
40
class RESTRunner(Runner):
43
 
    #intercept_signals = False
 
41
    # Don't install the standard signal handlers because as defined, they
 
42
    # won't actually stop the TCPServer started by .serve_forever().
44
43
    is_queue_runner = False
45
44
 
 
45
    def __init__(self, name, slice=None):
 
46
        """See `IRunner`."""
 
47
        super(RESTRunner, self).__init__(name, slice)
 
48
        # Both the REST server and the signal handlers must run in the main
 
49
        # thread; the former because of SQLite requirements (objects created
 
50
        # in one thread cannot be shared with the other threads), and the
 
51
        # latter because of Python's signal handling semantics.
 
52
        #
 
53
        # Unfortunately, we cannot issue a TCPServer shutdown in the main
 
54
        # thread, because that will cause a deadlock.  Yay.   So what we do is
 
55
        # to use the signal handler to notify a shutdown thread that the
 
56
        # shutdown should happen.  That thread will wake up and stop the main
 
57
        # server.
 
58
        self._server = make_server()
 
59
        self._event = threading.Event()
 
60
        def stopper(event, server):
 
61
            event.wait()
 
62
            server.shutdown()
 
63
        self._thread = threading.Thread(
 
64
            target=stopper, args=(self._event, self._server))
 
65
        self._thread.start()
 
66
 
46
67
    def run(self):
47
 
        log.info('Starting REST server')
48
 
        # Handle SIGTERM the same way as SIGINT.
49
 
        def stop_server(signum, frame):
50
 
            log.info('REST server shutdown')
51
 
            sys.exit(signal.SIGTERM)
52
 
        signal.signal(signal.SIGTERM, stop_server)
53
 
        try:
54
 
            make_server().serve_forever()
55
 
        except KeyboardInterrupt:
56
 
            log.info('REST server interrupted')
57
 
            sys.exit(signal.SIGINT)
58
 
        except select.error as (errcode, message):
59
 
            if errcode == errno.EINTR:
60
 
                log.info('REST server exiting')
61
 
                sys.exit(errno.EINTR)
62
 
            raise
63
 
        except:
64
 
            raise
 
68
        """See `IRunner`."""
 
69
        self._server.serve_forever()
 
70
 
 
71
    def signal_handler(self, signum, frame):
 
72
        super(RESTRunner, self).signal_handler(signum, frame)
 
73
        if signum in (signal.SIGTERM, signal.SIGINT, signal.SIGUSR1):
 
74
            # Set the flag that will terminate the TCPserver loop.
 
75
            self._event.set()
 
76
 
 
77
    def _one_iteration(self):
 
78
        # Just keep going
 
79
        if self._thread.is_alive():
 
80
            self._thread.join(timeout=0.1)
 
81
        return 1
 
82
 
 
83
    def _snooze(self, filecnt):
 
84
        pass