diff -r f9391e2b74a5 Lib/socketserver.py --- a/Lib/socketserver.py Fri Feb 19 08:57:50 2016 +0100 +++ b/Lib/socketserver.py Fri Feb 19 18:37:45 2016 +0200 @@ -208,6 +208,7 @@ self.RequestHandlerClass = RequestHandlerClass self.__is_shut_down = threading.Event() self.__shutdown_request = False + self.__closed = False def server_activate(self): """Called by constructor to activate the server. @@ -233,7 +234,7 @@ with _ServerSelector() as selector: selector.register(self, selectors.EVENT_READ) - while not self.__shutdown_request: + while not self.__shutdown_request and not self.__closed: ready = selector.select(poll_interval) if ready: self._handle_request_noblock() @@ -347,12 +348,8 @@ self.shutdown_request(request) def server_close(self): - """Called to clean-up the server. - - May be overridden. - - """ - pass + """Called to clean-up the server.""" + self.__closed = True def finish_request(self, request, client_address): """Finish one request by instantiating RequestHandlerClass.""" @@ -474,6 +471,7 @@ """ self.socket.close() + super().server_close() def fileno(self): """Return socket file number. diff -r f9391e2b74a5 Lib/test/test_socketserver.py --- a/Lib/test/test_socketserver.py Fri Feb 19 08:57:50 2016 +0100 +++ b/Lib/test/test_socketserver.py Fri Feb 19 18:37:45 2016 +0200 @@ -280,6 +280,17 @@ socketserver.TCPServer((HOST, -1), socketserver.StreamRequestHandler) + def test_server_close_stop_serve_forever(self): + s = socketserver.TCPServer((HOST, 0), socketserver.StreamRequestHandler) + t = threading.Thread( + target=s.serve_forever, + kwargs={'poll_interval': 0.01}) + t.daemon = True # In case this function raises. + t.start() + s.server_close() + t.join(0.02) + self.assertEqual(t.is_alive(), False) + class MiscTestCase(unittest.TestCase):