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