Browse Source

Refine the logic of serve_forever() and shutdown().

git-svn-id: svn://bind10.isc.org/svn/bind10/branches/trac352@3298 e5f2f494-b856-4b98-b285-d166d9295462
Likun Zhang 14 years ago
parent
commit
f8facdc96e

+ 16 - 30
src/lib/python/isc/utils/serve_mixin.py

@@ -21,7 +21,7 @@ SOCK_DATA = b'somedata'
 class ServeMixIn:
 class ServeMixIn:
     '''Mix-In class to override the function serve_forever()
     '''Mix-In class to override the function serve_forever()
     and shutdown() in class socketserver::TCPServer.
     and shutdown() in class socketserver::TCPServer.
-      serve_forever() in socketserver::TCPServer use polling
+      serve_forever() in socketserver.TCPServer use polling
     for checking request, which reduces the responsiveness to
     for checking request, which reduces the responsiveness to
     the shutdown request and wastes cpu at all other times.
     the shutdown request and wastes cpu at all other times.
       ServeMixIn should be used together with socketserver.TCPServer
       ServeMixIn should be used together with socketserver.TCPServer
@@ -31,53 +31,39 @@ class ServeMixIn:
     explicitely in derived class.
     explicitely in derived class.
     '''
     '''
     def __init__(self):
     def __init__(self):
-        self.__serving = False
         self.__read_sock, self.__write_sock = socket.socketpair()
         self.__read_sock, self.__write_sock = socket.socketpair()
-        self.__serve_thread = None
+        self.__is_shut_down = threading.Event()
 
 
     def serve_forever(self, poll_interval=None):
     def serve_forever(self, poll_interval=None):
         ''' Override the serve_forever([poll_interval]) in class
         ''' Override the serve_forever([poll_interval]) in class
-        socketserver.TCPServer by using the socketpair to wake up
-        instead of polling.
+        socketserver.TCPServer. use the socketpair to wake up
+        the select when shutdown() is called in anther thread.
           Note, parameter 'poll_interval' is just used to keep the
           Note, parameter 'poll_interval' is just used to keep the
         interface, it's never used in this function.
         interface, it's never used in this function.
         '''        
         '''        
-        self.__serving = True
-        started_event = threading.Event()
-
-        self.__serve_thread = threading.Thread(target=self.__serve_forever, \
-                                               args=(started_event,))
-        self.__serve_thread.start()
-        
-        started_event.wait() # wait until the thread has started
-        return self.__serve_thread
-
-    def __serve_forever(self, syn_event):
-        '''Use one socket pair to wake up the select when shutdown() 
-        is called in anther thread.
-        '''        
-        self.__serving = True
-        syn_event.set()
-
-        while self.__serving:
+        while True:
             # block until the self.socket or self.__read_sock is readable
             # block until the self.socket or self.__read_sock is readable
             try:
             try:
                 r, w, e = select.select([self, self.__read_sock], [], [])
                 r, w, e = select.select([self, self.__read_sock], [], [])
-            except select.error:
+            except select.error as err:
+                if err.args[0] == EINTR:
                     continue
                     continue
+                else:
+                    sys.stderr.write("Error with select(), %s\n", err)
+                    break
             
             
             if self.__read_sock in r:
             if self.__read_sock in r:
                 break
                 break
             else:
             else:
                 self._handle_request_noblock()
                 self._handle_request_noblock()
 
 
+        self.__is_shut_down.set()                
+
     def shutdown(self):
     def shutdown(self):
-        '''Stops the self.__serve_thread( self.__serve_forever loop).
-        when self.__serve_thread is running, it will block until the 
-        self.__serve_thread terminate.
+        '''Stops the serve_forever loop.
+        Blocks until the loop has finished, the function should be called
+        in another thread when serve_forever is running, or it will block.
         '''
         '''
-        self.__serving = False
         self.__write_sock.send(SOCK_DATA) # make self.__read_sock readable.
         self.__write_sock.send(SOCK_DATA) # make self.__read_sock readable.
-        if self.__serve_thread:
-            self.__serve_thread.join() # wait until the serve thread terminate
+        self.__is_shut_down.wait()  # wait until the serve thread terminate
 
 

+ 4 - 1
src/lib/python/isc/utils/tests/serve_mixin_test.py

@@ -46,7 +46,9 @@ class TestServeMixIn(unittest.TestCase):
         # use port 0 to select an arbitrary unused port.
         # use port 0 to select an arbitrary unused port.
         server = MyServer(('127.0.0.1', 0), MyHandler)
         server = MyServer(('127.0.0.1', 0), MyHandler)
         ip, port = server.server_address
         ip, port = server.server_address
-        server_thread = server.serve_forever()
+        server_thread = threading.Thread(target=server.serve_forever)
+        server_thread.setDaemon(True)
+        server_thread.start()
 
 
         msg = b'senddata'
         msg = b'senddata'
         self.assertEqual(msg, send_and_get_reply(ip, port, msg))
         self.assertEqual(msg, send_and_get_reply(ip, port, msg))
@@ -55,6 +57,7 @@ class TestServeMixIn(unittest.TestCase):
         # Now shutdown the server
         # Now shutdown the server
         server.shutdown()
         server.shutdown()
         # Sleep a while, make sure the thread has finished.
         # Sleep a while, make sure the thread has finished.
+        time.sleep(0.1)
         self.assertFalse(server_thread.is_alive())
         self.assertFalse(server_thread.is_alive())
 
 
 if __name__== "__main__":
 if __name__== "__main__":