Search code examples
pythonmultithreadingserversocketasyncsocket

Stopping socketserver.ThreadingMixIn in python


I'm extending socketserver.ThreadingMixIn in Python 3.4 to build my own threaded server while keeping the original callbacks overwintered only for logging porpoises. The activation and creation is very simple and according to python documentation the problem i'm having is to stop that server with server.shutdown(). It frizzes and doesn't exit. I need way to shutdown that server without using ctrl-c because it will also involve some GUI to that server.

The basic server:

class ServerBasic(socketserver.ThreadingMixIn,socketserver.TCPServer):

logging.basicConfig(level=logging.DEBUG,format='%(name)s: %(message)s',)   

def __init__(self, log_name,server_address, handler_class=ThreadedRequestHandler):
    self.logger = logging.getLogger(log_name)
    self.logger.debug('__init__')
    socketserver.TCPServer.__init__(self, server_address, handler_class)
    return

def server_activate(self):
    self.logger.debug('server_activate')
    socketserver.TCPServer.server_activate(self)
    return

def serve_forever(self):
    self.logger.debug('waiting for request')
    self.logger.info('Handling requests, press <Ctrl-C> to quit')
    while True:
        self.handle_request()
    return

The extending class:

class ManagerServer(PIRServerBasic):


def __init__(self, log_name, handler_class=T_ManagerRequestHandler):
    self.tup_socket = (ipAddress, WELCOME_PORT) # tuple of the address and port
    self.log_name = log_name
    return ServerBasic.__init__(self, log_name, self.tup_socket, handler_class=handler_class)

And here how it all created and running:

o_serverManager = ManagerServer('Manager_Server', T_ManagerRequestHandler) 
t_managerServer = threading.Thread(target=o_serverManager.serve_forever)
t_managerServer.daemon = True 
t_managerServer.start()

sleep(15)
o_serverManager.shutdown()

After the shutdown command the program is stuck.


Solution

  • In your overwrite of the serve_forever method, you have removed the condition that breaks the while loop on a shutdown request. The original method looks like:

    def serve_forever(self, poll_interval=0.5):
        """Handle one request at a time until shutdown.
    
        Polls for shutdown every poll_interval seconds. Ignores
        self.timeout. If you need to do periodic tasks, do them in
        another thread.
        """
        self.__is_shut_down.clear()
        try:
            while not self.__shutdown_request:
                r, w, e = _eintr_retry(select.select, [self], [], [],
                                       poll_interval)
                if self in r:
                    self._handle_request_noblock()
        finally:
            self.__shutdown_request = False
            self.__is_shut_down.set()
    

    You need to implement a similar system in your overwrite, to look for a set __shutdown_request flag and take the appropriate action. This also requires that your handler is non-blocking.