--- a/Lib/socketserver.py 2011-12-24 01:31:40.513853443 -0500 +++ b/Lib/socketserver.py 2011-12-24 12:17:16.225591163 -0500 @@ -196,9 +196,14 @@ """Constructor. May be extended, do not override.""" self.server_address = server_address self.RequestHandlerClass = RequestHandlerClass - self.__is_shut_down = threading.Event() self.__shutdown_request = False self.__running = False + self.post_init() + + def post_init(self): + """To be overridden by derived classes, specifically mixins which + cannot call super().""" + pass def __repr__(self): info = [] @@ -233,22 +238,32 @@ if self.running: raise RuntimeError("the server is already running") self.__running = True - self.__is_shut_down.clear() + self.set_shutdown(False) try: while not self.__shutdown_request: # XXX: Consider using another file descriptor or # connecting to the socket to wake this up instead of # polling. Polling reduces our responsiveness to a # shutdown request and wastes cpu at all other times. - r, w, e = select.select([self], [], [], poll_interval) + try: + r, w, e = select.select([self], [], [], poll_interval) + except select.error: + r = [] if self in r: self._handle_request_noblock() finally: self.__shutdown_request = False self.__running = False - self.__is_shut_down.set() + self.set_shutdown(True) def shutdown(self): + """Frontend call to _shutdown() to stop serve_forever. + + Will be overridden by derived classes. + """ + self._shutdown() + + def _shutdown(self): """Stops the serve_forever loop. Blocks until the loop has finished. This must be called while @@ -258,7 +273,11 @@ if not self.running: raise RuntimeError("the server is not running") self.__shutdown_request = True - self.__is_shut_down.wait() + + def set_shutdown(self, value): + """Indicate if the system should be shut down.""" + # to be overridden by derived classes + pass # The distinction between handling, getting, processing and # finishing a request is fairly arbitrary. Remember: @@ -502,6 +521,22 @@ active_children = None max_children = 40 + import signal as _sigmod + if hasattr(_sigmod, 'SIGURG'): + def post_init(self): + self._sigmod.signal(self._sigmod.SIGURG, self.sighandler) + + def sighandler(self, signo, tb): + self._shutdown() + self.post_init() + + def shutdown(self): + if self.running: + os.kill(os.getppid(), self._sigmod.SIGURG) + else: + def shutdown(self): + raise AssertionError('ForkingMixIn.shutdown() is not supported') + def collect_children(self): """Internal routine to wait for children that have exited.""" if self.active_children is None: return @@ -572,6 +607,22 @@ # main process daemon_threads = False + def post_init(self): + self.__is_shut_down = threading.Event() + + def shutdown(self): + """Calls the shutdown() method of of the super class, then + waits for the BaseServer__is_shut_down.wait() call.""" + # cannot use super() since this is a classic class, not a new style + self._shutdown() + self.__is_shut_down.wait() + + def set_shutdown(self, value): + if value: + self.__is_shut_down.set() + else: + self.__is_shut_down.clear() + def process_request_thread(self, request, client_address): """Same as in BaseServer but as a thread.