Problem in multithreading Socketserver explained (should apply to *nix, too)

Michael Abbott michael at rcp.co.uk
Wed Aug 29 11:28:02 EDT 2001


gerson.kurz at t-online.de (Gerson Kurz) wrote in 
news:3b8c84e1.766674781 at news.isar.de:

> The BaseHTTPServer is derived from SocketServer.TCPServer.
> This class serves up data using the handle_request() member function.
> It looks like this (comments added by me):
>     
> -----------------------
> def handle_request(self):
>     try:
>         request, client_address = self.get_request()
>     except socket.error:
>         return
>     if self.verify_request(request, client_address):
>         try:
>             # this will start a new thread
>             self.process_request(request, client_address)
>         except:
>             self.handle_error(request, client_address)
>     # this will close the socket still in use by the new thread
>     self.close_request(request)
> -----------------------
> The ThreadingTCPServer, which can be mixed in at a higher point in the
> hierarchy, overloads self.process_request() to startup a new thread
> for that request. However, once the thread has started, if the thread
> running self.handle_request() gets the CPU before the other thread has
> finished reading from the request, it will close the request, even
> while a thread serving the request is running in the background.
> And, self.close_request() closes the socket, so the error message
> about _sock being a number is completely explainable. 
> 
> So I don't think just adding ThreadingTCPServer could've worked from
> the start; and the same problem could possibly happen on the *nix
> implementation of ThreadingTCPServer, too.
> 
> Solution: 
> 
> 1) the derived TCPServer has to implement close_request(self) as a
> stub function
> 2) the request class has to implement handle() and at the end of the
> function, close the socket.
> 

This is very timely for me, many thanks for the diagnosis.  I'm not sure I 
agree with your solution, though.  Surely all of the processing after the 
request has been received should be split off into another thread?

Here's my solution:

class ThreadedHTTPServer(BaseHTTPServer.HTTPServer):

    thread_pool = ThreadPool.ThreadPool(5, onThreadStartup)

    # We override handle_request so that all of the processing for one
    # request can be done in a separate thread.
    #     Unfortunately, we can't use the clever ThreadingMixIn technique, 
    # because the implementation of SocketServer.BaseServer is broken.  In
    # particular, close_request() can't be called until the spawned thread
    # has completed processing!

    def handle_request(self):
        # This bit stolen wholesale from SocketServer
        try:
            request, client_address = self.get_request()
        except socket.error:
            return

        # We do all the rest in its own thread
        thread = threading.Thread(
            target=self.handle_request_body, 
            args=(request, client_address))
        thread.start()


    # This part of the processing is run in its own thread
    def handle_request_body(self, request, client_address):
        # Again, this bit's thieved from SocketServer
        if self.verify_request(request, client_address):
            try:
                self.process_request(request, client_address)
            except:
                self.handle_error(request, client_address)
        self.close_request(request)




More information about the Python-list mailing list