Rev 6206: Merge bzr-2.4 into bzr.dev, resolve the small conflicts. in http://bazaar.launchpad.net/~jameinel/bzr/2.4-into-2.5

John Arbash Meinel john at arbash-meinel.com
Mon Oct 10 13:59:42 UTC 2011


At http://bazaar.launchpad.net/~jameinel/bzr/2.4-into-2.5

------------------------------------------------------------
revno: 6206 [merge]
revision-id: john at arbash-meinel.com-20111010135922-wanbhsy9i42zm6w9
parent: pqm at pqm.ubuntu.com-20111010085906-wh17ij8w2k3bxs6x
parent: pqm at pqm.ubuntu.com-20111010100601-ckp3ys7hobu3elq2
committer: John Arbash Meinel <john at arbash-meinel.com>
branch nick: 2.4-into-2.5
timestamp: Mon 2011-10-10 15:59:22 +0200
message:
  Merge bzr-2.4 into bzr.dev, resolve the small conflicts.
modified:
  bzrlib/tests/test_server.py    test_server.py-20100209163834-im1ozfuenfmqaa2m-1
  bzrlib/tests/test_test_server.py test_test_server.py-20100601152414-r8rln0ok7514pcoz-1
  doc/en/release-notes/bzr-2.4.txt bzr2.4.txt-20110114053217-k7ym9jfz243fddjm-1
-------------- next part --------------
=== modified file 'bzrlib/tests/test_server.py'
--- a/bzrlib/tests/test_server.py	2011-10-05 15:01:54 +0000
+++ b/bzrlib/tests/test_server.py	2011-10-10 13:59:22 +0000
@@ -289,7 +289,6 @@
 
     def serve(self):
         self.serving = True
-        self.stopped.clear()
         # We are listening and ready to accept connections
         self.started.set()
         try:
@@ -464,6 +463,8 @@
         if debug_threads():
             sys.stderr.write('Client thread %s started\n' % (t.name,))
         # If an exception occured during the thread start, it will get raised.
+        # In rare cases, an exception raised during the request processing may
+        # also get caught here (see http://pad.lv/869366)
         t.pending_exception()
 
     # The following methods are called by the main thread
@@ -519,7 +520,7 @@
             sync_event=self.server.started,
             target=self.run_server)
         self._server_thread.start()
-        # Wait for the server thread to start (i.e release the lock)
+        # Wait for the server thread to start (i.e. release the lock)
         self.server.started.wait()
         # Get the real address, especially the port
         self.host, self.port = self.server.server_address

=== modified file 'bzrlib/tests/test_test_server.py'
--- a/bzrlib/tests/test_test_server.py	2011-10-03 08:15:39 +0000
+++ b/bzrlib/tests/test_test_server.py	2011-10-10 13:59:22 +0000
@@ -112,9 +112,6 @@
         for name in
         ('TestingTCPServer', 'TestingThreadingTCPServer')]
 
-    # Set by load_tests()
-    server_class = None
-
     def get_server(self, server_class=None, connection_handler_class=None):
         if server_class is not None:
             self.server_class = server_class
@@ -202,16 +199,26 @@
         self.assertRaises(CantConnect, server.stop_server)
 
     def test_server_crash_while_responding(self):
-        sync = threading.Event()
-        sync.clear()
+        # We want to ensure the exception has been caught
+        caught = threading.Event()
+        caught.clear()
+        # The thread that will serve the client, this needs to be an attribute
+        # so the handler below can modify it when it's executed (it's
+        # instantiated when the request is processed)
+        self.connection_thread = None
+
         class FailToRespond(Exception):
             pass
 
         class FailingDuringResponseHandler(TCPConnectionHandler):
 
-            def handle_connection(self):
-                req = self.readline()
-                threading.currentThread().set_sync_event(sync)
+            def handle_connection(request):
+                req = request.readline()
+                # Capture the thread and make it use 'caught' so we can wait on
+                # the even that will be set when the exception is caught. We
+                # also capture the thread to know where to look.
+                self.connection_thread = threading.currentThread()
+                self.connection_thread.set_sync_event(caught)
                 raise FailToRespond()
 
         server = self.get_server(
@@ -219,22 +226,46 @@
         client = self.get_client()
         client.connect((server.host, server.port))
         client.write('ping\n')
-        sync.wait()
+        # Wait for the exception to be caught
+        caught.wait()
         self.assertEqual('', client.read()) # connection closed
-        self.assertRaises(FailToRespond, server.pending_exception)
+        # Check that the connection thread did catch the exception,
+        # http://pad.lv/869366 was wrongly checking the server thread which
+        # works for TestingTCPServer where the connection is handled in the
+        # same thread than the server one but is racy for
+        # TestingThreadingTCPServer where the server thread may be in a
+        # blocking accept() call (or not).
+        try:
+            self.connection_thread.pending_exception()
+        except FailToRespond:
+            # Great, the test succeeded
+            pass
+        else:
+            # If the exception is not in the connection thread anymore, it's in
+            # the server's one. 
+            server.server.stopped.wait()
+            # The exception is available now
+            self.assertRaises(FailToRespond, server.pending_exception)
 
     def test_exception_swallowed_while_serving(self):
-        sync = threading.Event()
-        sync.clear()
+        # We need to ensure the exception has been caught
+        caught = threading.Event()
+        caught.clear()
+        # The thread that will serve the client, this needs to be an attribute
+        # so the handler below can access it when it's executed (it's
+        # instantiated when the request is processed)
+        self.connection_thread = None
         class CantServe(Exception):
             pass
 
         class FailingWhileServingConnectionHandler(TCPConnectionHandler):
 
-            def handle(self):
-                # We want to sync with the thread that is serving the
-                # connection.
-                threading.currentThread().set_sync_event(sync)
+            def handle(request):
+                # Capture the thread and make it use 'caught' so we can wait on
+                # the even that will be set when the exception is caught. We
+                # also capture the thread to know where to look.
+                self.connection_thread = threading.currentThread()
+                self.connection_thread.set_sync_event(caught)
                 raise CantServe()
 
         server = self.get_server(
@@ -244,11 +275,16 @@
         client = self.get_client()
         # Connect to the server so the exception is raised there
         client.connect((server.host, server.port))
-        # Wait for the exception to propagate.
-        sync.wait()
+        # Wait for the exception to be caught
+        caught.wait()
         self.assertEqual('', client.read()) # connection closed
         # The connection wasn't served properly but the exception should have
-        # been swallowed.
+        # been swallowed (see test_server_crash_while_responding remark about
+        # http://pad.lv/869366 explaining why we can't check the server thread
+        # here). More precisely, the exception *has* been caught and captured
+        # but it is cleared when joining the thread (or trying to acquire the
+        # exception) and as such won't propagate to the server thread.
+        self.connection_thread.pending_exception()
         server.pending_exception()
 
     def test_handle_request_closes_if_it_doesnt_process(self):

=== modified file 'doc/en/release-notes/bzr-2.4.txt'
--- a/doc/en/release-notes/bzr-2.4.txt	2011-10-06 16:41:45 +0000
+++ b/doc/en/release-notes/bzr-2.4.txt	2011-10-10 13:59:22 +0000
@@ -75,6 +75,9 @@
    suite.  This can include new facilities for writing tests, fixes to 
    spurious test failures and changes to the way things should be tested.
 
+* Fix the race for TestingThreadingTCPServer in
+  test_server_crash_while_responding. (Vincent Ladeuil, #869366)
+
 * Really corrupt the pack file without depending on a special length or value.
   (Vincent Ladeuil, #807032)
 



More information about the bazaar-commits mailing list