Commit 8a50c401 authored by Tim Peters's avatar Tim Peters

Merge rev 30894 from 3.4 branch.

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.
parent b51ff3e4
What's new in ZODB3 3.5a3? What's new in ZODB3 3.5a4?
========================== ==========================
Release date: 17-Jun-2005 Release date: DD-MMM-2005
Following is combined news from "internal releases" (to support ongoing Following is combined news from "internal releases" (to support ongoing
Zope3 development). These are the dates of the internal releases: Zope3 development). These are the dates of the internal releases:
- 3.5a3 DD-MMM-2005 - 3.5a4 DD-MMM-2005
- 3.5a3 17-Jun-2005
- 3.5a2 16-Jun-2005 - 3.5a2 16-Jun-2005
- 3.5a1 10-Jun-2005 - 3.5a1 10-Jun-2005
...@@ -27,6 +28,18 @@ Multi-database ...@@ -27,6 +28,18 @@ Multi-database
been added. See ``ZODB/cross-database-references.txt`` for an been added. See ``ZODB/cross-database-references.txt`` for an
introduction. introduction.
ThreadedAsync.LoopCallback
--------------------------
- (3.5a4) 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).
FileStorage.UndoSearch FileStorage.UndoSearch
---------------------- ----------------------
...@@ -63,6 +76,18 @@ FileStorage.UndoSearch ...@@ -63,6 +76,18 @@ FileStorage.UndoSearch
very valuable when you want to spot strange transaction sizes via Zope's very valuable when you want to spot strange transaction sizes via Zope's
'Undo' tab". '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? What's new in ZODB3 3.4?
======================== ========================
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# FOR A PARTICULAR PURPOSE # 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 In a multi-threaded application, only a single thread runs the
asyncore mainloop. This thread (the "mainloop thread") may not start asyncore mainloop. This thread (the "mainloop thread") may not start
...@@ -27,27 +27,16 @@ socket map as its first argument. ...@@ -27,27 +27,16 @@ socket map as its first argument.
""" """
import asyncore import asyncore
import select
import thread import thread
import time
from errno import EINTR _original_asyncore_loop = asyncore.loop
_loop_lock = thread.allocate_lock() _loop_lock = thread.allocate_lock()
_looping = None _looping = None # changes to socket map when loop() starts
_loop_callbacks = [] _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): 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 The callable object callback will be invokved when the mainloop
starts. If the mainloop is currently running, the callback will starts. If the mainloop is currently running, the callback will
...@@ -72,15 +61,21 @@ def remove_loop_callback(callback): ...@@ -72,15 +61,21 @@ def remove_loop_callback(callback):
This is useful if loop() was never called. This is useful if loop() was never called.
""" """
for i in range(len(_loop_callbacks)): for i, value in enumerate(_loop_callbacks):
if _loop_callbacks[i][0] == callback: if value[0] == callback:
del _loop_callbacks[i] del _loop_callbacks[i]
return 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() _loop_lock.acquire()
try: try:
global _looping
_looping = map _looping = map
while _loop_callbacks: while _loop_callbacks:
cb, args, kw = _loop_callbacks.pop() cb, args, kw = _loop_callbacks.pop()
...@@ -88,96 +83,24 @@ def _start_loop(map): ...@@ -88,96 +83,24 @@ def _start_loop(map):
finally: finally:
_loop_lock.release() _loop_lock.release()
def _stop_loop(): result = _original_asyncore_loop(*args, **kws)
_loop_lock.acquire() _loop_lock.acquire()
try: try:
global _looping
_looping = None _looping = None
finally: finally:
_loop_lock.release() _loop_lock.release()
def poll(timeout=0.0, map=None): return result
"""A copy of asyncore.poll() with a bug fixed (see comment).
(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()
# Evil: rebind asyncore.loop to the above loop() function.
# 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!
# #
# Code should instead explicitly call ThreadedAsync.loop() instead of # Code should explicitly call ThreadedAsync.loop() instead of asyncore.loop().
# asyncore.loop(). Most of ZODB has been fixed, but ripping this out may # Most of ZODB has been fixed, but ripping this out may break 3rd party code.
# break 3rd party code. So we'll issue a warning and let it continue -- for # Maybe we should issue a warning and let it continue for a while. Or
# now. # 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): ##def deprecated_loop(*args, **kws):
## import warnings ## import warnings
...@@ -186,7 +109,7 @@ def loop(timeout=30.0, use_poll=0, map=None): ...@@ -186,7 +109,7 @@ def loop(timeout=30.0, use_poll=0, map=None):
##You should change your code to call ThreadedAsync.loop() explicitly.""", ##You should change your code to call ThreadedAsync.loop() explicitly.""",
## DeprecationWarning) ## DeprecationWarning)
## loop(*args, **kws) ## loop(*args, **kws)
##
##asyncore.loop = deprecated_loop ##asyncore.loop = deprecated_loop
asyncore.loop = loop asyncore.loop = loop
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