Commit 5a0ec5cc authored by Patrick Strawderman's avatar Patrick Strawderman

Bug fix: the ConnectionManager could hang if its ConnectThread died

without setting the connection.
parent da1820a5
...@@ -1519,6 +1519,32 @@ def quick_close_doesnt_kill_server(): ...@@ -1519,6 +1519,32 @@ def quick_close_doesnt_kill_server():
>>> db.close() >>> db.close()
""" """
def sync_connect_doesnt_hang():
r"""
>>> import threading
>>> import ZEO.zrpc.client
>>> ConnectThread = ZEO.zrpc.client.ConnectThread
>>> ZEO.zrpc.client.ConnectThread = lambda *a, **kw: threading.Thread()
>>> class CM(ZEO.zrpc.client.ConnectionManager):
... sync_wait = 1
... _start_asyncore_loop = lambda self: None
>>> cm = CM('', object())
Calling connect results in an exception being raised, instead of hanging
indefinitely when the thread dies without setting up the connection.
>>> cm.connect(sync=1)
Traceback (most recent call last):
...
AssertionError
>>> cm.thread.isAlive()
False
>>> ZEO.zrpc.client.ConnectThread = ConnectThread
"""
slow_test_classes = [ slow_test_classes = [
BlobAdaptedFileStorageTests, BlobWritableCacheTests, BlobAdaptedFileStorageTests, BlobWritableCacheTests,
...@@ -1535,7 +1561,7 @@ class ServerManagingClientStorage(ClientStorage): ...@@ -1535,7 +1561,7 @@ class ServerManagingClientStorage(ClientStorage):
class StorageServerStubClass(ZEO.ServerStub.StorageServer): class StorageServerStubClass(ZEO.ServerStub.StorageServer):
# Wait for abort for the benefit of blob_transacton.txt # Wait for abort for the benefit of blob_transaction.txt
def tpc_abort(self, id): def tpc_abort(self, id):
self.rpc.call('tpc_abort', id) self.rpc.call('tpc_abort', id)
......
...@@ -131,6 +131,8 @@ def client_loop(map): ...@@ -131,6 +131,8 @@ def client_loop(map):
class ConnectionManager(object): class ConnectionManager(object):
"""Keeps a connection up over time""" """Keeps a connection up over time"""
sync_wait = 30
def __init__(self, addrs, client, tmin=1, tmax=180): def __init__(self, addrs, client, tmin=1, tmax=180):
self.client = client self.client = client
self._start_asyncore_loop() self._start_asyncore_loop()
...@@ -273,14 +275,13 @@ class ConnectionManager(object): ...@@ -273,14 +275,13 @@ class ConnectionManager(object):
t.setDaemon(1) t.setDaemon(1)
t.start() t.start()
if sync: if sync:
while self.connection is None: while self.connection is None and t.isAlive():
self.cond.wait(30) self.cond.wait(self.sync_wait)
if self.connection is None: if self.connection is None:
log("CM.connect(sync=1): still waiting...") log("CM.connect(sync=1): still waiting...")
assert self.connection is not None
finally: finally:
self.cond.release() self.cond.release()
if sync:
assert self.connection is not None
def connect_done(self, conn, preferred): def connect_done(self, conn, preferred):
# Called by ConnectWrapper.notify_client() after notifying the client # Called by ConnectWrapper.notify_client() after notifying the client
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment