[Zodb-checkins] CVS: ZEO/ZEO/zrpc - client.py:1.1.2.3

Jeremy Hylton jeremy@zope.com
Fri, 25 Jan 2002 19:02:43 -0500


Update of /cvs-repository/ZEO/ZEO/zrpc
In directory cvs.zope.org:/tmp/cvs-serv24474

Modified Files:
      Tag: Standby-branch
	client.py 
Log Message:
Fix KeyError bug (I think).

The problem was in the logic of attempt_connects().  It has two parts:
one where it creates the sockets and initiates the connection, another
where it waits for the first connection to succeed.  But it's possible
that the connect returns right away.  In this case, the socket wasn't
put in the sockets dict yet and the cleanup code failed with a
KeyError.

As part of the fix, I reworked the entire handling of the sockets
dict.  I made it an instance variable so that attempt_connects() has
to do less explicit management of it.  The various helper methods can
modify it directly.


=== ZEO/ZEO/zrpc/client.py 1.1.2.2 => 1.1.2.3 ===
         self.stopped = 1
 
+    def close_sockets(self):
+        for s in self.sockets.keys():
+            s.close()
+
     def run(self):
         delay = self.tmin
         while not (self.stopped or self.attempt_connects()):
@@ -163,7 +167,7 @@
                 
     def attempt_connects(self):
         "Return true if any connect attempt succeeds."
-        sockets = {}
+        self.sockets = {}
 
         log("attempting connection on %d sockets" % len(self.addr))
         try:
@@ -173,35 +177,30 @@
                         level=zLOG.DEBUG)
                 s = socket.socket(domain, socket.SOCK_STREAM)
                 s.setblocking(0)
+                self.sockets[s] = addr
                 # XXX can still block for a while if addr requires DNS
-                e = self.connect(s, addr)
-                if e is not None:
-                    sockets[s] = addr
+                e = self.connect(s)
 
             # next wait until they actually connect
-            while sockets:
+            while self.sockets:
                 if self.stopped:
-                    for s in sockets.keys():
-                        s.close()
+                    self.close_sockets()
                     return 0
                 try:
-                    r, w, x = select.select([], sockets.keys(), [], 1.0)
+                    r, w, x = select.select([], self.sockets.keys(), [], 1.0)
                 except select.error:
                     continue
                 for s in w:
-                    e = self.connect(s, sockets[s])
-                    if e is None:
-                        del sockets[s]
+                    # connect() will raise Connected if it succeeds
+                    e = self.connect(s)
         except Connected, container:
             s = container.sock
-            del sockets[s]
-            # close all the other sockets
-            for s in sockets.keys():
-                s.close()
+            del self.sockets[s] # don't close the newly connected socket
+            self.close_sockets()
             return 1
         return 0
 
-    def connect(self, s, addr):
+    def connect(self, s):
         """Call s.connect_ex(addr) and return true if loop should continue.
 
         We have to handle several possible return values from
@@ -213,10 +212,12 @@
 
         If the socket sonnects and the initial ZEO setup fails or the
         connect_ex() returns an error, we close the socket and ignore it.
+        When the socket is closed, it is removed from self.sockets.
 
         If connect_ex() returns EINPROGRESS, we need to try again later.
         """
-        
+
+        addr = self.sockets[s]
         e = s.connect_ex(addr)
         if e == errno.EINPROGRESS:
             return 1
@@ -230,6 +231,7 @@
                 log("error connecting to %s: %s" % (addr, errno.errorcode[e]),
                     level=zLOG.DEBUG)
             s.close()
+            del self.sockets[s]
 
     def test_connection(self, s, addr):
         c = ManagedConnection(s, addr, self.client, self.mgr)