Commit ec80a2e8 authored by Guido van Rossum's avatar Guido van Rossum

A bunch of renamings and other small changes to make the code a little

more understandable.

Got rid of the Connected() exception -- connect(), now renamed to
try_connect(), returns a 1/0 result instead of raising Connected() to
indicate success.
parent 9f0ad321
...@@ -29,8 +29,8 @@ from ZEO.zrpc.connection import ManagedConnection ...@@ -29,8 +29,8 @@ from ZEO.zrpc.connection import ManagedConnection
class ConnectionManager: class ConnectionManager:
"""Keeps a connection up over time""" """Keeps a connection up over time"""
def __init__(self, addr, client, tmin=1, tmax=180): def __init__(self, addrs, client, tmin=1, tmax=180):
self.set_addr(addr) self.addrlist = self._parse_addrs(addrs)
self.client = client self.client = client
self.tmin = tmin self.tmin = tmin
self.tmax = tmax self.tmax = tmax
...@@ -46,26 +46,27 @@ class ConnectionManager: ...@@ -46,26 +46,27 @@ class ConnectionManager:
ThreadedAsync.register_loop_callback(self.set_async) ThreadedAsync.register_loop_callback(self.set_async)
def __repr__(self): def __repr__(self):
return "<%s for %s>" % (self.__class__.__name__, self.addr) return "<%s for %s>" % (self.__class__.__name__, self.addrlist)
def set_addr(self, addr): def _parse_addrs(self, addrs):
"Set one or more addresses to use for server." # Return a list of (addr_type, addr) pairs.
# For backwards compatibility (and simplicity?) the # For backwards compatibility (and simplicity?) the
# constructor accepts a single address in the addr argument -- # constructor accepts a single address in the addrs argument --
# a string for a Unix domain socket or a 2-tuple with a # a string for a Unix domain socket or a 2-tuple with a
# hostname and port. It can also accept a list of such addresses. # hostname and port. It can also accept a list of such addresses.
addr_type = self._guess_type(addr) addr_type = self._guess_type(addrs)
if addr_type is not None: if addr_type is not None:
self.addr = [(addr_type, addr)] return [(addr_type, addrs)]
else: else:
self.addr = [] addrlist = []
for a in addr: for addr in addrs:
addr_type = self._guess_type(a) addr_type = self._guess_type(addr)
if addr_type is None: if addr_type is None:
raise ValueError, "unknown address in list: %s" % repr(a) raise ValueError, "unknown address in list: %s" % repr(a)
self.addr.append((addr_type, a)) addrlist.append((addr_type, addr))
return addrlist
def _guess_type(self, addr): def _guess_type(self, addr):
if isinstance(addr, types.StringType): if isinstance(addr, types.StringType):
...@@ -146,7 +147,8 @@ class ConnectionManager: ...@@ -146,7 +147,8 @@ class ConnectionManager:
t = self.thread t = self.thread
if t is None: if t is None:
log("starting thread to connect to server") log("starting thread to connect to server")
self.thread = t = ConnectThread(self, self.client, self.addr, self.thread = t = ConnectThread(self, self.client,
self.addrlist,
self.tmin, self.tmax) self.tmin, self.tmax)
t.start() t.start()
finally: finally:
...@@ -175,11 +177,6 @@ class ConnectionManager: ...@@ -175,11 +177,6 @@ class ConnectionManager:
if not self.closed: if not self.closed:
self.connect() self.connect()
class Connected(Exception):
# helper for non-local exit
def __init__(self, sock):
self.sock = sock
# When trying to do a connect on a non-blocking socket, some outcomes # When trying to do a connect on a non-blocking socket, some outcomes
# are expected. Set _CONNECT_IN_PROGRESS to the errno value(s) expected # are expected. Set _CONNECT_IN_PROGRESS to the errno value(s) expected
# when an initial connect can't complete immediately. Set _CONNECT_OK # when an initial connect can't complete immediately. Set _CONNECT_OK
...@@ -209,11 +206,11 @@ class ConnectThread(threading.Thread): ...@@ -209,11 +206,11 @@ class ConnectThread(threading.Thread):
# We don't expect clients to call any methods of this Thread other # We don't expect clients to call any methods of this Thread other
# than close() and those defined by the Thread API. # than close() and those defined by the Thread API.
def __init__(self, mgr, client, addrs, tmin, tmax): def __init__(self, mgr, client, addrlist, tmin, tmax):
self.__super_init(name="Connect(%s)" % addrs) self.__super_init(name="Connect(%s)" % addrlist)
self.mgr = mgr self.mgr = mgr
self.client = client self.client = client
self.addrs = addrs self.addrlist = addrlist
self.tmin = tmin self.tmin = tmin
self.tmax = tmax self.tmax = tmax
self.stopped = 0 self.stopped = 0
...@@ -248,7 +245,7 @@ class ConnectThread(threading.Thread): ...@@ -248,7 +245,7 @@ class ConnectThread(threading.Thread):
s.close() s.close()
def attempt_connects(self): def attempt_connects(self):
"""Try connecting to all self.addrs addresses. """Try connecting to all self.addrlist addresses.
If at least one succeeds, pick a success arbitrarily, close all other If at least one succeeds, pick a success arbitrarily, close all other
successes (if any), and return true. If none succeed, return false. successes (if any), and return true. If none succeed, return false.
...@@ -256,49 +253,50 @@ class ConnectThread(threading.Thread): ...@@ -256,49 +253,50 @@ class ConnectThread(threading.Thread):
self.sockets = {} # {open socket: connection address} self.sockets = {} # {open socket: connection address}
log("attempting connection on %d sockets" % len(self.addrs)) log("attempting connection on %d sockets" % len(self.addrlist))
try: ok = 0
for domain, addr in self.addrs: for domain, addr in self.addrlist:
if __debug__: if __debug__:
log("attempt connection to %s" % repr(addr), log("attempt connection to %s" % repr(addr),
level=zLOG.DEBUG) level=zLOG.DEBUG)
try: try:
s = socket.socket(domain, socket.SOCK_STREAM) s = socket.socket(domain, socket.SOCK_STREAM)
except socket.error, err: except socket.error, err:
log("Failed to create socket with domain=%s: %s" % ( log("Failed to create socket with domain=%s: %s" % (
domain, err), level=zLOG.ERROR) domain, err), level=zLOG.ERROR)
continue continue
s.setblocking(0) s.setblocking(0)
self.sockets[s] = addr self.sockets[s] = addr
# connect() raises Connected iff it succeeds # XXX can still block for a while if addr requires DNS
# XXX can still block for a while if addr requires DNS if self.try_connect(s):
self.connect(s) ok = 1
break
# next wait until they actually connect
while self.sockets: # next wait until they actually connect
if self.stopped: while not ok and self.sockets:
self.close_sockets() if self.stopped:
return 0 self.close_sockets()
try: return 0
sockets = self.sockets.keys() try:
r, w, x = select.select([], sockets, sockets, 1.0) sockets = self.sockets.keys()
except select.error: r, w, x = select.select([], sockets, sockets, 1.0)
continue except select.error:
for s in x: continue
del self.sockets[s] for s in x:
s.close() del self.sockets[s]
for s in w: s.close()
# connect() raises Connected iff it succeeds for s in w:
self.connect(s) if self.try_connect(s):
except Connected, container: ok = 1
s = container.sock break
if ok:
del self.sockets[s] # don't close the newly connected socket del self.sockets[s] # don't close the newly connected socket
self.close_sockets() self.close_sockets()
return 1 return ok
return 0
def connect(self, s): def try_connect(self, s):
"""Call s.connect_ex(addr); raise Connected iff connection succeeds. """Call s.connect_ex(addr); return true iff connection succeeds.
We have to handle several possible return values from We have to handle several possible return values from
connect_ex(). If the socket is connected and the initial ZEO connect_ex(). If the socket is connected and the initial ZEO
...@@ -323,13 +321,13 @@ class ConnectThread(threading.Thread): ...@@ -323,13 +321,13 @@ class ConnectThread(threading.Thread):
else: else:
log("connect_ex(%s) == %s" % (addr, e)) log("connect_ex(%s) == %s" % (addr, e))
if e in _CONNECT_IN_PROGRESS: if e in _CONNECT_IN_PROGRESS:
return return 0
elif e in _CONNECT_OK: elif e in _CONNECT_OK:
# special cases to deal with winsock oddities # special cases to deal with winsock oddities
if sys.platform.startswith("win") and e == 0: if sys.platform.startswith("win") and e == 0:
# It appears that winsock isn't behaving as # It appears that winsock isn't behaving as
# expected on Win2k. It's possible for connect() # expected on Win2k. It's possible for connect_ex()
# to return 0, but the connection to have failed. # to return 0, but the connection to have failed.
# In particular, in situations where I expect to # In particular, in situations where I expect to
# get a Connection refused (10061), I'm seeing # get a Connection refused (10061), I'm seeing
...@@ -339,7 +337,7 @@ class ConnectThread(threading.Thread): ...@@ -339,7 +337,7 @@ class ConnectThread(threading.Thread):
r, w, x = select.select([s], [s], [s], 0.1) r, w, x = select.select([s], [s], [s], 0.1)
if not (r or w or x): if not (r or w or x):
return return 0
if x: if x:
# see comment at the end of the function # see comment at the end of the function
s.close() s.close()
...@@ -347,7 +345,7 @@ class ConnectThread(threading.Thread): ...@@ -347,7 +345,7 @@ class ConnectThread(threading.Thread):
c = self.test_connection(s, addr) c = self.test_connection(s, addr)
if c: if c:
log("connected to %s" % repr(addr), level=zLOG.DEBUG) log("connected to %s" % repr(addr), level=zLOG.DEBUG)
raise Connected(s) return 1
else: else:
log("error connecting to %s: %s" % (addr, errno.errorcode[e]), log("error connecting to %s: %s" % (addr, errno.errorcode[e]),
level=zLOG.DEBUG) level=zLOG.DEBUG)
...@@ -357,6 +355,7 @@ class ConnectThread(threading.Thread): ...@@ -357,6 +355,7 @@ class ConnectThread(threading.Thread):
# sockets. # sockets.
s.close() s.close()
del self.sockets[s] del self.sockets[s]
return 0
def test_connection(self, s, addr): def test_connection(self, s, addr):
# Establish a connection at the zrpc level and call the # Establish a connection at the zrpc level and call the
......
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