diff -r f9391e2b74a5 Lib/socketserver.py --- a/Lib/socketserver.py Fri Feb 19 08:57:50 2016 +0100 +++ b/Lib/socketserver.py Sat Feb 20 11:48:34 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. @@ -234,6 +235,8 @@ selector.register(self, selectors.EVENT_READ) while not self.__shutdown_request: + if self.__closed: + raise ValueError("Operation on closed server") ready = selector.select(poll_interval) if ready: self._handle_request_noblock() @@ -347,12 +350,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 +473,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 Sat Feb 20 11:48:34 2016 +0200 @@ -280,6 +280,27 @@ socketserver.TCPServer((HOST, -1), socketserver.StreamRequestHandler) + def test_server_close_stop_serve_forever(self): + + class MyServer(socketserver.TCPServer): + serve_forever_throw = False + + def test_serve_forever_throws(server): + try: + server.serve_forever(0.01) + except ValueError: + server.serve_forever_throw = True + + s = MyServer((HOST, 0), socketserver.StreamRequestHandler) + t = threading.Thread( + target=test_serve_forever_throws, + args=(s,)) + 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):