Rev 6188: Now we've implemented the basic structure. in http://bazaar.launchpad.net/~jameinel/bzr/2.5-soft-hangup-795025

John Arbash Meinel john at arbash-meinel.com
Fri Sep 23 16:40:42 UTC 2011


At http://bazaar.launchpad.net/~jameinel/bzr/2.5-soft-hangup-795025

------------------------------------------------------------
revno: 6188
revision-id: john at arbash-meinel.com-20110923164032-15duj1g3hj49ww53
parent: john at arbash-meinel.com-20110923160023-lr49ty2wn70nqu91
committer: John Arbash Meinel <john at arbash-meinel.com>
branch nick: 2.5-soft-hangup-795025
timestamp: Fri 2011-09-23 18:40:32 +0200
message:
  Now we've implemented the basic structure.
  
  I tested it manually, and it works as we wanted, the server waits for the current
  request to stop, but keeps running until it does so. Once the current request
  has finished, it closes the connection to the client.
  
  There are now a couple of race conditions in the test suite. I caught a couple
  of them, like the connection thread was being started before we added it to
  the list of active connections.
  The next race that I know of, is the test suite expects that a request&response
  finish is enough for us to have run _poll_active_connections.
  However, there is no actual interlocking there.
  So we actually need to do a new connection, which may give us 1-or-2 active
  connections, but then we can check if the old handler is still around.
-------------- next part --------------
=== modified file 'bzrlib/smart/medium.py'
--- a/bzrlib/smart/medium.py	2011-09-23 13:01:12 +0000
+++ b/bzrlib/smart/medium.py	2011-09-23 16:40:32 +0000
@@ -48,7 +48,7 @@
 from bzrlib.smart import client, protocol, request, signals, vfs
 from bzrlib.transport import ssh
 """)
-from bzrlib import config, osutils
+from bzrlib import osutils
 
 # Throughout this module buffer size parameters are either limited to be at
 # most _MAX_READ_SIZE, or are ignored and _MAX_READ_SIZE is used instead.
@@ -242,6 +242,8 @@
                 if server_protocol is None:
                     # We could 'continue' only to notice that self.finished is
                     # True...
+                    trace.mutter('Stopped while waiting for request: %s\n'
+                                 % (self,))
                     break
                 self._serve_one_request(server_protocol)
         except errors.ConnectionTimeout, e:

=== modified file 'bzrlib/smart/server.py'
--- a/bzrlib/smart/server.py	2011-09-23 16:00:23 +0000
+++ b/bzrlib/smart/server.py	2011-09-23 16:40:32 +0000
@@ -172,6 +172,8 @@
         trace.note('Requested to stop gracefully')
         self._should_terminate = True
         self._gracefully_stopping = True
+        for handler, _ in self._active_connections:
+            handler._stop_gracefully()
 
     def _wait_for_clients_to_disconnect(self):
         self._poll_active_connections()
@@ -230,14 +232,14 @@
                 trace.report_exception(sys.exc_info(), sys.stderr)
                 raise
         finally:
-            self._stopped.set()
-            signals.unregister_on_hangup(id(self))
             try:
                 # ensure the server socket is closed.
                 self._server_socket.close()
             except self._socket_error:
                 # ignore errors on close
                 pass
+            self._stopped.set()
+            signals.unregister_on_hangup(id(self))
             self.run_server_stopped_hooks()
         if self._gracefully_stopping:
             self._wait_for_clients_to_disconnect()
@@ -263,10 +265,10 @@
         :return: None
         """
         still_active = []
-        for conn, handler, thread in self._active_connections:
+        for handler, thread in self._active_connections:
             thread.join(timeout)
             if thread.isAlive():
-                still_active.append((conn, handler, thread))
+                still_active.append((handler, thread))
         self._active_connections = still_active
 
     def serve_conn(self, conn, thread_name_suffix):
@@ -278,12 +280,9 @@
         handler = self._make_handler(conn)
         connection_thread = threading.Thread(
             None, handler.serve, name=thread_name)
-        # FIXME: This thread is never joined, it should at least be collected
-        # somewhere so that tests that want to check for leaked threads can get
-        # rid of them -- vila 20100531
+        self._active_connections.append((handler, connection_thread))
         connection_thread.setDaemon(True)
         connection_thread.start()
-        self._active_connections.append((conn, handler, connection_thread))
         return connection_thread
 
     def start_background_thread(self, thread_name_suffix=''):

=== modified file 'bzrlib/tests/test_smart_transport.py'
--- a/bzrlib/tests/test_smart_transport.py	2011-09-23 16:00:23 +0000
+++ b/bzrlib/tests/test_smart_transport.py	2011-09-23 16:40:32 +0000
@@ -18,12 +18,15 @@
 
 # all of this deals with byte strings so this is safe
 from cStringIO import StringIO
+import doctest
 import os
 import socket
 import sys
 import threading
 import time
 
+from testtools.matchers import DocTestMatches
+
 import bzrlib
 from bzrlib import (
         bzrdir,
@@ -1131,6 +1134,7 @@
         server._stop_gracefully()
         self.connect_to_server_and_hangup(server)
         server._stopped.wait()
+        server._fully_stopped.wait()
         server_thread.join()
 
     def test_get_error_unexpected(self):
@@ -1187,7 +1191,7 @@
         self.say_hello(client_sock)
         self.assertEqual(1, len(server._active_connections))
         # Grab a handle to the thread that is processing our request
-        _, _, server_side_thread = server._active_connections[0]
+        _, server_side_thread = server._active_connections[0]
         # Close the connection, ask the server to stop, and wait for the
         # server to stop, as well as the thread that was servicing the
         # client request.
@@ -1208,7 +1212,7 @@
         # results.
         self.say_hello(client_sock1)
         self.assertEqual(1, len(server._active_connections))
-        _, _, server_side_thread1 = server._active_connections[0]
+        _, server_side_thread1 = server._active_connections[0]
         client_sock1.close()
         server_side_thread1.join()
         # By waiting until the first connection is fully done, the server
@@ -1216,7 +1220,7 @@
         client_sock2 = self.connect_to_server(server)
         self.say_hello(client_sock2)
         self.assertEqual(1, len(server._active_connections))
-        _, _, server_side_thread2 = server._active_connections[0]
+        _, server_side_thread2 = server._active_connections[0]
         client_sock2.close()
         server_side_thread2.join()
         self.shutdown_server_cleanly(server, server_thread)
@@ -1225,7 +1229,7 @@
         server, server_thread = self.make_server()
         client_sock = self.connect_to_server(server)
         self.say_hello(client_sock)
-        _, _, server_side_thread = server._active_connections[0]
+        _, server_side_thread = server._active_connections[0]
         # Ask the server to stop gracefully, and wait for it.
         server._stop_gracefully()
         self.connect_to_server_and_hangup(server)
@@ -1240,9 +1244,24 @@
         server_thread.join()
         self.assertTrue(server._fully_stopped.isSet())
         log = self.get_log()
-        self.assertEqual('    INFO  Requested to stop gracefully\n'
-                         '    INFO  Waiting for 1 client(s) to finish\n',
-                         log)
+        self.assertThat(log, DocTestMatches("""\
+    INFO  Requested to stop gracefully
+... Stopping <bzrlib.smart.medium.SmartServerSocketStreamMedium ...
+    INFO  Waiting for 1 client(s) to finish
+... Stopped while waiting for request: ...
+""", flags=doctest.ELLIPSIS|doctest.REPORT_UDIFF))
+
+    def test_stop_gracefully_tells_handlers_to_stop(self):
+        server, server_thread = self.make_server()
+        client_sock = self.connect_to_server(server)
+        self.say_hello(client_sock)
+        server_handler, server_side_thread = server._active_connections[0]
+        self.assertFalse(server_handler.finished)
+        server._stop_gracefully()
+        self.assertTrue(server_handler.finished)
+        client_sock.close()
+        self.connect_to_server_and_hangup(server)
+        server_thread.join()
 
 
 class SmartTCPTests(tests.TestCase):



More information about the bazaar-commits mailing list