[Zope-Checkins] CVS: Zope2 - ThreadedAsync.py:1.4
jeremy@digicool.com
jeremy@digicool.com
Mon, 26 Mar 2001 16:49:35 -0500 (EST)
Update of /cvs-repository/Zope2/lib/python
In directory korak:/tmp/cvs-serv1544
Modified Files:
ThreadedAsync.py
Log Message:
Fix bug in register_loop_callback(). If the mainloop had already
started, calls to it would fail with a NameError (map). The fix is to
using the global _looping to hold the socket map used by the current
loop() call.
Also reformat the code and add some doc strings.
--- Updated File ThreadedAsync.py in package Zope2 --
--- ThreadedAsync.py 2000/05/16 16:59:50 1.3
+++ ThreadedAsync.py 2001/03/26 21:49:35 1.4
@@ -82,51 +82,95 @@
# attributions are listed in the accompanying credits file.
#
##############################################################################
-"""Utility module to help 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
+the mainloop before another thread needs to perform an async action
+that requires it. As a result, other threads need to coordinate with
+the mainloop thread to find out whether the mainloop is running.
+
+This module implements a callback mechanism that allows other threads
+to be notified when the mainloop starts. A thread calls
+register_loop_callback() to register interest. When the mainloop
+thread calls loop(), each registered callback will be called with the
+socket map as its first argument.
+
+This module rebinds loop() in the asyncore module; i.e. once this
+module is imported, any client of the asyncore module will get
+ThreadedAsync.loop() when it calls asyncore.loop().
"""
__version__='$Revision$'[11:-2]
-import thread
import asyncore
+import select
+import thread
-_loop_lock=thread.allocate_lock()
-_looping=0
-_loop_callbacks=[]
+_loop_lock = thread.allocate_lock()
+_looping = None
+_loop_callbacks = []
def register_loop_callback(callback, args=(), kw=None):
+ """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
+ be invoked immediately.
+
+ The callback will be called with a single argument, the mainloop
+ socket map, unless the optional args or kw arguments are used.
+ args defines a tuple of extra arguments to pass after the socket
+ map. kw defines a dictionary of keyword arguments.
+ """
_loop_lock.acquire()
try:
- if _looping: apply(callback, (map,)+args, kw or {})
- else: _loop_callbacks.append((callback, args, kw))
- finally: _loop_lock.release()
+ if _looping is not None:
+ apply(callback, (_looping,) + args, kw or {})
+ else:
+ _loop_callbacks.append((callback, args, kw))
+ finally:
+ _loop_lock.release()
def _start_loop(map):
_loop_lock.acquire()
try:
global _looping
- _looping=1
+ _looping = map
while _loop_callbacks:
cb, args, kw = _loop_callbacks.pop()
- apply(cb, (map,)+args, kw or {})
-
- finally: _loop_lock.release()
+ apply(cb, (map,) + args, kw or {})
+ finally:
+ _loop_lock.release()
def _stop_loop():
_loop_lock.acquire()
try:
global _looping
- _looping=0
- finally: _loop_lock.release()
+ _looping = None
+ finally:
+ _loop_lock.release()
def loop (timeout=30.0, use_poll=0, map=None):
+ """Invoke asyncore mainloop
- if use_poll: poll_fun = asyncore.poll2
- else: poll_fun = asyncore.poll
+ This function functions like the regular asyncore.loop() function
+ except that it also triggers ThreadedAsync callback functions
+ before starting the loop.
+ """
+ if use_poll:
+ if hasattr(select, 'poll'):
+ poll_fun = asyncore.poll3
+ else:
+ poll_fun = asyncore.poll2
+ else:
+ poll_fun = asyncore.poll
- if map is None: map=asyncore.socket_map
+ if map is None:
+ map = asyncore.socket_map
_start_loop(map)
- while map: poll_fun (timeout, map)
+ while map:
+ poll_fun(timeout, map)
_stop_loop()
# Woo hoo!