[Zope-Checkins] CVS: ZODB3/ZEO/zrpc - client.py:1.19.2.4

Jeremy Hylton jeremy@zope.com
Tue, 29 Apr 2003 11:23:58 -0400


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

Modified Files:
      Tag: ZODB3-3_1-branch
	client.py 
Log Message:
Backport two fixes.

Add timeout to try_connecting() method.

Fix a bug in _create_wrappers() according to a suggestion by Barry
Pederson.  The 'wrap' local variable was reused in a way that
overwrote the value intended to set the return dictionary.


=== ZODB3/ZEO/zrpc/client.py 1.19.2.3 => 1.19.2.4 ===
--- ZODB3/ZEO/zrpc/client.py:1.19.2.3	Wed Dec 18 16:14:33 2002
+++ ZODB3/ZEO/zrpc/client.py	Tue Apr 29 11:23:57 2003
@@ -283,10 +283,14 @@
     def run(self):
         delay = self.tmin
         success = 0
+        # Don't wait too long the first time.
+        # XXX make timeout configurable?
+        attempt_timeout = 5
         while not self.stopped:
-            success = self.try_connecting()
+            success = self.try_connecting(attempt_timeout)
             if not self.one_attempt.isSet():
                 self.one_attempt.set()
+                attempt_timeout = 75
             if success > 0:
                 break
             time.sleep(delay)
@@ -296,30 +300,58 @@
             delay = min(delay*2, self.tmax)
         log("CT: exiting thread: %s" % self.getName())
 
-    def try_connecting(self):
+    def try_connecting(self, timeout):
         """Try connecting to all self.addrlist addresses.
 
         Return 1 if a preferred connection was found; 0 if no
         connection was found; and -1 if a fallback connection was
         found.
-        """
 
+        If no connection is found within timeout seconds, return 0.
+        """
         log("CT: attempting to connect on %d sockets" % len(self.addrlist))
+        deadline = time.time() + timeout
+        wrappers = self._create_wrappers()
+        for wrap in wrappers.keys():
+            if wrap.state == "notified":
+                return 1
+        try:
+            if time.time() > deadline:
+                return 0
+            r = self._connect_wrappers(wrappers, deadline)
+            if r is not None:
+                return r
+            if time.time() > deadline:
+                return 0
+            r = self._fallback_wrappers(wrappers, deadline)
+            if r is not None:
+                return r
+            # Alas, no luck.
+            assert not wrappers
+        finally:
+            for wrap in wrappers.keys():
+                wrap.close()
+            del wrappers
+        return 0
 
+    def _create_wrappers(self):
         # Create socket wrappers
         wrappers = {}  # keys are active wrappers
         for domain, addr in self.addrlist:
             wrap = ConnectWrapper(domain, addr, self.mgr, self.client)
             wrap.connect_procedure()
             if wrap.state == "notified":
-                for wrap in wrappers.keys():
-                    wrap.close()
-                return 1
+                for w in wrappers.keys():
+                    w.close()
+                return {wrap: wrap}
             if wrap.state != "closed":
                 wrappers[wrap] = wrap
+        return wrappers
 
+    def _connect_wrappers(self, wrappers, deadline):
         # Next wait until they all actually connect (or fail)
-        # XXX If a sockets never connects, nor fails, we'd wait forever!
+        # The deadline is necessary, because we'd wait forever if a
+        # sockets never connects or fails.
         while wrappers:
             if self.stopped:
                 for wrap in wrappers.keys():
@@ -331,8 +363,11 @@
                           if wrap.state == "connecting"]
             if not connecting:
                 break
+            if time.time() > deadline:
+                break
             try:
                 r, w, x = select.select([], connecting, connecting, 1.0)
+                log("CT: select() %d, %d, %d" % tuple(map(len, (r,w,x))))
             except select.error, msg:
                 log("CT: select failed; msg=%s" % str(msg),
                     level=zLOG.WARNING) # XXX Is this the right level?
@@ -353,6 +388,7 @@
                 if wrap.state == "closed":
                     del wrappers[wrap]
 
+    def _fallback_wrappers(self, wrappers, deadline):
         # If we've got wrappers left at this point, they're fallback
         # connections.  Try notifying them until one succeeds.
         for wrap in wrappers.keys():
@@ -369,9 +405,8 @@
             assert wrap.state == "closed"
             del wrappers[wrap]
 
-        # Alas, no luck.
-        assert not wrappers
-        return 0
+            # XXX should check deadline
+        
 
 class ConnectWrapper:
     """An object that handles the connection procedure for one socket.