[Zodb-checkins] SVN: ZODB/branches/3.4/ Largely rewritten.

Tim Peters tim.one at comcast.net
Wed Jun 22 18:04:30 EDT 2005


Log message for revision 30894:
  Largely rewritten.
  
  remove_loop_callback():  There were two definitions of this function.
  Deleted one, changed the other to use enumerate().
  
  poll():  This was trying to repair a bug in Python 2.2's asyncore.poll().
  2.2 is no longer supported, so removed this poll().
  
  _start_loop(), _stop_loop():  Removed; functionality folded into
  reworked loop().
  
  loop():  The signature of asyncore.loop() changed in 2.4, but this
  still had "the old" signature.  Instead of sniffing the signature
  of the Python in use, this does a saner thing:  the original
  asyncore.loop is captured, and called from the body of this loop().
  That way, (A) we don't care what asyncore.loop's signature is; and,
  (B) we still call the original Python code, so debugging prints and
  breakpoints (etc) stuffed into Python's asyncore no longer "vanish
  by magic" when ZEO is used.
  

Changed:
  U   ZODB/branches/3.4/NEWS.txt
  U   ZODB/branches/3.4/src/ThreadedAsync/LoopCallback.py

-=-
Modified: ZODB/branches/3.4/NEWS.txt
===================================================================
--- ZODB/branches/3.4/NEWS.txt	2005-06-22 22:03:13 UTC (rev 30893)
+++ ZODB/branches/3.4/NEWS.txt	2005-06-22 22:04:30 UTC (rev 30894)
@@ -24,7 +24,19 @@
   very valuable when you want to spot strange transaction sizes via Zope's
   'Undo' tab".
 
+ThreadedAsync.LoopCallback
+--------------------------
 
+- This replaces Python's ``asyncore.loop`` function with its own, in order
+  to get notified when ``loop()`` is first called.  The signature of
+  ``asyncore.loop`` changed in Python 2.4, but ``LoopCallback.loop``'s
+  signature didn't change to match.  The code here was repaired to be
+  compatible with both old and new signatures, and also repaired to invoke
+  Python's ``asyncore.loop()`` instead of replacing it entirely (so, for
+  example, debugging prints added to Python's ``asyncore.loop`` won't be
+  lost anymore).
+
+
 What's new in ZODB3 3.4?
 ========================
 Release date: 09-Jun-2005

Modified: ZODB/branches/3.4/src/ThreadedAsync/LoopCallback.py
===================================================================
--- ZODB/branches/3.4/src/ThreadedAsync/LoopCallback.py	2005-06-22 22:03:13 UTC (rev 30893)
+++ ZODB/branches/3.4/src/ThreadedAsync/LoopCallback.py	2005-06-22 22:04:30 UTC (rev 30894)
@@ -11,7 +11,7 @@
 # FOR A PARTICULAR PURPOSE
 #
 ##############################################################################
-"""Manage the asyncore mainloop in a multi-threaded app
+"""Manage the asyncore mainloop in a multi-threaded app.
 
 In a multi-threaded application, only a single thread runs the
 asyncore mainloop.  This thread (the "mainloop thread") may not start
@@ -27,27 +27,16 @@
 """
 
 import asyncore
-import select
 import thread
-import time
-from errno import EINTR
 
+_original_asyncore_loop = asyncore.loop
+
 _loop_lock = thread.allocate_lock()
-_looping = None
+_looping = None # changes to socket map when loop() starts
 _loop_callbacks = []
 
-def remove_loop_callback(callback):
-    """Remove a callback function registered earlier.
-
-    This is useful if loop() was never called.
-    """
-    for i in range(len(_loop_callbacks)):
-        if _loop_callbacks[i][0] == callback:
-            del _loop_callbacks[i]
-            return
-
 def register_loop_callback(callback, args=(), kw=None):
-    """Register callback function to be called when mainloop starts
+    """Register callback function to be called when mainloop starts.
 
     The callable object callback will be invokved when the mainloop
     starts.  If the mainloop is currently running, the callback will
@@ -72,15 +61,21 @@
 
     This is useful if loop() was never called.
     """
-    for i in range(len(_loop_callbacks)):
-        if _loop_callbacks[i][0] == callback:
+    for i, value in enumerate(_loop_callbacks):
+        if value[0] == callback:
             del _loop_callbacks[i]
             return
 
-def _start_loop(map):
+# Caution:  the signature of asyncore.loop changed in Python 2.4.
+# That's why we use `args` and `kws` instead of spelling out the
+# "intended" arguments.  Since we _replace_ asyncore.loop with this
+# loop(), we need to be compatible with all signatures.
+def loop(*args, **kws):
+    global _looping
+
+    map = kws.get("map", asyncore.socket_map)
     _loop_lock.acquire()
     try:
-        global _looping
         _looping = map
         while _loop_callbacks:
             cb, args, kw = _loop_callbacks.pop()
@@ -88,96 +83,24 @@
     finally:
         _loop_lock.release()
 
-def _stop_loop():
+    result = _original_asyncore_loop(*args, **kws)
+
     _loop_lock.acquire()
     try:
-        global _looping
         _looping = None
     finally:
         _loop_lock.release()
 
-def poll(timeout=0.0, map=None):
-    """A copy of asyncore.poll() with a bug fixed (see comment).
+    return result
 
-    (asyncore.poll2() and .poll3() don't have this bug.)
-    """
-    if map is None:
-        map = asyncore.socket_map
-    if map:
-        r = []; w = []; e = []
-        for fd, obj in map.items():
-            if obj.readable():
-                r.append(fd)
-            if obj.writable():
-                w.append(fd)
-        if [] == r == w == e:
-            time.sleep(timeout)
-        else:
-            try:
-                r, w, e = select.select(r, w, e, timeout)
-            except select.error, err:
-                if err[0] != EINTR:
-                    raise
-                else:
-                    # This part is missing in asyncore before Python 2.3
-                    return
 
-        for fd in r:
-            obj = map.get(fd)
-            if obj is not None:
-                try:
-                    obj.handle_read_event()
-                except asyncore.ExitNow:
-                    raise asyncore.ExitNow
-                except:
-                    obj.handle_error()
-
-        for fd in w:
-            obj = map.get(fd)
-            if obj is not None:
-                try:
-                    obj.handle_write_event()
-                except asyncore.ExitNow:
-                    raise asyncore.ExitNow
-                except:
-                    obj.handle_error()
-
-def loop(timeout=30.0, use_poll=0, map=None):
-    """Invoke asyncore mainloop
-
-    This function functions like the regular asyncore.loop() function
-    except that it also triggers ThreadedAsync callback functions
-    before starting the loop.
-    """
-    global exit_status
-    exit_status = None
-
-    if use_poll:
-        if hasattr(select, 'poll'):
-            poll_fun = asyncore.poll3
-        else:
-            poll_fun = asyncore.poll2
-    else:
-        poll_fun = poll
-
-    if map is None:
-        map = asyncore.socket_map
-
-    _start_loop(map)
-    while map and exit_status is None:
-        poll_fun(timeout, map)
-    _stop_loop()
-
-
-# This module used to do something evil -- it rebound asyncore.loop to the
-# above loop() function.  What was evil about this is that if you added some
-# debugging to asyncore.loop, you'd spend 6 hours debugging why your debugging
-# code wasn't called!
+# Evil:  rebind asyncore.loop to the above loop() function.
 #
-# Code should instead explicitly call ThreadedAsync.loop() instead of
-# asyncore.loop().  Most of ZODB has been fixed, but ripping this out may
-# break 3rd party code.  So we'll issue a warning and let it continue -- for
-# now.
+# Code should explicitly call ThreadedAsync.loop() instead of asyncore.loop().
+# Most of ZODB has been fixed, but ripping this out may break 3rd party code.
+# Maybe we should issue a warning and let it continue for a while.  Or
+# maybe we should get rid of this mechanism entirely, and have each ZEO
+# piece that needs one run its own asyncore loop in its own thread.
 
 ##def deprecated_loop(*args, **kws):
 ##    import warnings
@@ -186,7 +109,7 @@
 ##You should change your code to call ThreadedAsync.loop() explicitly.""",
 ##                  DeprecationWarning)
 ##    loop(*args, **kws)
-
+##
 ##asyncore.loop = deprecated_loop
 
 asyncore.loop = loop



More information about the Zodb-checkins mailing list