David Rose 17 years ago
parent
commit
c6320cc83a

+ 2 - 0
direct/src/stdpy/Sources.pp

@@ -0,0 +1,2 @@
+// For now, since we are not installing Python files, this file can
+// remain empty.

+ 0 - 0
direct/src/stdpy/__init__.py


+ 261 - 0
direct/src/stdpy/thread.py

@@ -0,0 +1,261 @@
+""" This module reimplements Python's native thread module using Panda
+threading constructs.  It's designed as a drop-in replacement for the
+thread module for code that works with Panda; it is necessary because
+in some compilation models, Panda's threading constructs are
+incompatible with the OS-provided threads used by Python's thread
+module. """
+
+__all__ = [
+    'error', 'LockType',
+    'start_new_thread',
+    'interrupt_main',
+    'exit', 'allocate_lock', 'get_ident',
+    'stack_size',
+    ]
+
+# Import PandaModules as pm, so we don't have any namespace collisions.
+from pandac import PandaModules as pm
+
+class error(StandardError):
+    pass
+
+class LockType:
+    """ Implements a mutex lock.  Instead of directly subclassing
+    PandaModules.Mutex, we reimplement the lock here, to allow us to
+    provide the described Python lock semantics.  In particular, this
+    allows a different thread to release the lock than the one that
+    acquired it. """
+    
+    def __init__(self):
+        self.__lock = pm.Mutex('PythonLock')
+        self.__cvar = pm.ConditionVar(self.__lock)
+        self.__locked = False
+
+    def acquire(self, waitflag = 1):
+        self.__lock.acquire()
+        try:
+            if self.__locked and not waitflag:
+                return False
+            while self.__locked:
+                self.__cvar.wait()
+                
+            self.__locked = True
+            return True
+
+        finally:
+            self.__lock.release()
+
+    def release(self):
+        self.__lock.acquire()
+        try:
+            if not self.__locked:
+                raise error, 'Releasing unheld lock.'
+                
+            self.__locked = False
+            self.__cvar.notify()
+
+        finally:
+            self.__lock.release()
+        
+    def locked(self):
+        return self.__locked
+    
+    __enter__ = acquire
+
+    def __exit__(self, t, v, tb):
+        self.release()
+
+_threads = {}
+_nextThreadId = 0
+_threadsLock = pm.Mutex('thread._threadsLock')
+
+def start_new_thread(function, args, kwargs = {}, name = None):
+    def threadFunc(threadId, function = function, args = args, kwargs = kwargs):
+        try:
+            try:
+                function(*args, **kwargs)
+            except SystemExit:
+                pass
+
+        finally:
+            _remove_thread_id(threadId)
+
+    global _nextThreadId
+    _threadsLock.acquire()
+    try:
+        threadId = _nextThreadId
+        _nextThreadId += 1
+
+        if name is None:
+            name = 'PythonThread-%s' % (threadId)
+            
+        thread = pm.PythonThread(threadFunc, [threadId], name, name)
+        thread.setPythonData(threadId)
+        _threads[threadId] = (thread, {}, None)
+        
+        thread.start(pm.TPNormal, False)
+        return threadId
+
+    finally:
+        _threadsLock.release()
+
+def _add_thread(thread, wrapper):
+    """ Adds the indicated pm.Thread object, with the indicated Python
+    wrapper, to the thread list.  Returns the new thread ID. """
+    
+    global _nextThreadId
+    _threadsLock.acquire()
+    try:
+        threadId = _nextThreadId
+        _nextThreadId += 1
+            
+        thread.setPythonData(threadId)
+        _threads[threadId] = (thread, {}, wrapper)
+        return threadId
+        
+    finally:
+        _threadsLock.release()
+
+def _get_thread_wrapper(thread, wrapperClass):
+    """ Returns the thread wrapper for the indicated thread.  If there
+    is not one, creates an instance of the indicated wrapperClass
+    instead. """
+
+    threadId = thread.getPythonData()
+    if threadId is None:
+        # The thread has never been assigned a threadId.  Go assign one.
+        
+        global _nextThreadId
+        _threadsLock.acquire()
+        try:
+            threadId = _nextThreadId
+            _nextThreadId += 1
+
+            thread.setPythonData(threadId)
+            wrapper = wrapperClass(thread, threadId)
+            _threads[threadId] = (thread, {}, wrapper)
+            return wrapper
+
+        finally:
+            _threadsLock.release()
+
+    else:
+        # The thread has been assigned a threadId.  Look for the wrapper.
+        _threadsLock.acquire()
+        try:
+            t, locals, wrapper = _threads[threadId]
+            assert t == thread
+            if wrapper is None:
+                wrapper = wrapperClass(thread, threadId)
+                _threads[threadId] = (thread, locals, wrapper)
+            return wrapper
+
+        finally:
+            _threadsLock.release()
+
+def _get_thread_locals(thread, i):
+    """ Returns the locals dictionary for the indicated thread.  If
+    there is not one, creates an empty dictionary. """
+
+    threadId = thread.getPythonData()
+    if threadId is None:
+        # The thread has never been assigned a threadId.  Go assign one.
+        
+        global _nextThreadId
+        _threadsLock.acquire()
+        try:
+            threadId = _nextThreadId
+            _nextThreadId += 1
+
+            thread.setPythonData(threadId)
+            locals = {}
+            _threads[threadId] = (thread, locals, None)
+            return locals.setdefault(i, {})
+
+        finally:
+            _threadsLock.release()
+
+    else:
+        # The thread has been assigned a threadId.  Get the locals.
+        _threadsLock.acquire()
+        try:
+            t, locals, wrapper = _threads[threadId]
+            assert t == thread
+            return locals.setdefault(i, {})
+
+        finally:
+            _threadsLock.release()
+
+
+def _remove_thread_id(threadId):
+    """ Removes the thread with the indicated ID from the thread list. """
+    
+    _threadsLock.acquire()
+    try:
+        thread, locals, wrapper = _threads[threadId]
+        assert thread.getPythonData() == threadId
+        del _threads[threadId]
+        thread.setPythonData(None)
+        
+    finally:
+        _threadsLock.release()
+
+
+def interrupt_main():
+    # TODO.
+    pass
+
+def exit():
+    raise SystemExit
+
+def allocate_lock():
+    return LockType()
+
+def get_ident():
+    return pm.Thread.getCurrentThread().this
+
+def stack_size(size = 0):
+    raise error
+
+
+class _local(object):
+    """ This class provides local thread storage using Panda's
+    threading system. """
+
+    def __del__(self):
+        i = id(self)
+
+        # Delete this key from all threads.
+        _threadsLock.acquire()
+        try:
+            for thread, locals, wrapper in _threads.values():
+                try:
+                    del locals[i]
+                except KeyError:
+                    pass
+
+        finally:
+            _threadsLock.release()
+
+    def __setattr__(self, key, value):
+        d = _get_thread_locals(pm.Thread.getCurrentThread(), id(self))
+        d[key] = value
+        
+##     def __getattr__(self, key):
+##         d = _get_thread_locals(pm.Thread.getCurrentThread(), id(self))
+##         try:
+##             return d[key]
+##         except KeyError:
+##             raise AttributeError
+
+    def __getattribute__(self, key):
+        d = _get_thread_locals(pm.Thread.getCurrentThread(), id(self))
+        if key == '__dict__':
+            return d
+        try:
+            return d[key]
+        except KeyError:
+            return object.__getattribute__(self, key)
+
+        
+        

+ 500 - 0
direct/src/stdpy/threading.py

@@ -0,0 +1,500 @@
+""" This module reimplements Python's native threading module using Panda
+threading constructs.  It's designed as a drop-in replacement for the
+threading module for code that works with Panda; it is necessary because
+in some compilation models, Panda's threading constructs are
+incompatible with the OS-provided threads used by Python's thread
+module.
+
+This module implements the threading module with a thin layer over
+Panda's threading constructs.  As such, the semantics are close to,
+but not precisely, the semantics documented for Python's standard
+threading module.  If you really do require strict adherence to
+Python's semantics, see the threading2 module instead.
+
+However, if you don't need such strict adherence to Python's original
+semantics, this module is probably a better choice.  It is likely to
+be slighly faster than the threading2 module (and even slightly faster
+than Python's own threading module).  It is also better integrated
+with Panda's threads, so that Panda's thread debug mechanisms will be
+easier to use and understand.
+
+It is permissible to mix-and-match both threading and threading2
+within the same application. """
+
+# We import PandaModules as the name pm, so we can avoid namespace
+# collisions between native Panda objects, and our own class
+# definitions in this module. """
+import direct
+from pandac import PandaModules as pm
+from direct.stdpy import thread as _thread
+import sys as _sys
+
+import weakref
+
+__all__ = [
+    'Thread',
+    'Lock', 'RLock',
+    'Condition',
+    'Semaphore', 'BoundedSemaphore',
+    'Event',
+    'Timer',
+    'local',
+    'current_thread', 'currentThread',
+    'enumerate', 'active_count', 'activeCount',
+    'settrace', 'setprofile', 'stack_size',
+    ]
+
+local = _thread._local
+
+class ThreadBase:
+    """ A base class for both Thread and ExternalThread in this
+    module. """
+
+    def __init__(self):
+        pass
+
+    def getName(self):
+        return self.name
+    
+    def is_alive(self):
+        return self.__thread.isStarted()
+    
+    def isAlive(self):
+        return self.__thread.isStarted()
+
+    def isDaemon(self):
+        return self.daemon
+
+    def setDaemon(self, daemon):
+        if self.is_alive():
+            raise RuntimeError
+        
+        self.__dict__['daemon'] = daemon
+        
+    def __setattr__(self, key, value):
+        if key == 'name':
+            self.setName(value)
+        elif key == 'ident':
+            raise AttributeError
+        elif key == 'daemon':
+            self.setDaemon(value)
+        else:
+            self.__dict__[key] = value
+
+class Thread(ThreadBase):
+    """ This class provides a wrapper around Panda's PythonThread
+    object.  The wrapper is designed to emulate Python's own
+    threading.Thread object. """
+
+    def __init__(self, group=None, target=None, name=None, args=(), kwargs={}):
+        ThreadBase.__init__(self)
+
+        assert group is None
+        self.__target = target
+        self.__args = args
+        self.__kwargs = kwargs
+
+        current = current_thread()
+        self.__dict__['daemon'] = current.daemon
+        self.__dict__['name'] = name
+
+        self.__thread = pm.PythonThread(self.run, None, name, name)
+        threadId = _thread._add_thread(self.__thread, weakref.proxy(self))
+        self.__dict__['ident'] = threadId
+
+    def __del__(self):
+        if _thread:
+            _thread._remove_thread_id(self.ident)
+
+    def start(self):
+        if self.__thread.isStarted():
+            raise RuntimeException
+        
+        if not self.__thread.start(pm.TPNormal, True):
+            raise RuntimeException
+
+    def run(self):
+        if _settrace_func:
+            _sys.settrace(_settrace_func)
+        if _setprofile_func:
+            _sys.setprofile(_setprofile_func)
+
+        self.__target(*self.__args, **self.__kwargs)
+        
+    def join(self, timeout = None):
+        # We don't support a timed join here, sorry.
+        assert timeout is None
+        self.__thread.join()
+        self.__thread = None
+
+    def setName(self, name):
+        self.__dict__['name'] = name
+        self.__thread.setName(name)
+
+class ExternalThread(ThreadBase):
+    """ Returned for a Thread object that wasn't created by this
+    interface. """
+    
+    def __init__(self, extThread, threadId):
+        ThreadBase.__init__(self)
+        
+        self.__thread = extThread
+        self.__dict__['daemon'] = True
+        self.__dict__['name'] = self.__thread.getName()
+        self.__dict__['ident'] = threadId
+
+    def start(self):
+        raise RuntimeException
+    
+    def run(self):
+        raise RuntimeException
+        
+    def join(self, timeout = None):
+        raise RuntimeException
+
+    def setDaemon(self, daemon):
+        raise RuntimeError
+
+class MainThread(ExternalThread):
+    """ Returned for the MainThread object. """
+    
+    def __init__(self, extThread, threadId):
+        ExternalThread.__init__(self, extThread, threadId)
+        self.__dict__['daemon'] = False
+
+class Lock(pm.Mutex):
+    """ This class provides a wrapper around Panda's Mutex object.
+    The wrapper is designed to emulate Python's own threading.Lock
+    object. """
+
+    def __init__(self, name = "PythonLock"):
+        pm.Mutex.__init__(self, name)
+
+    def acquire(self, blocking = True):
+        if blocking:
+            pm.Mutex.acquire(self)
+            return True
+        else:
+            return pm.Mutex.tryAcquire(self)
+    
+    __enter__ = acquire
+
+    def __exit__(self, t, v, tb):
+        self.release()
+
+class RLock(pm.ReMutex):
+    """ This class provides a wrapper around Panda's ReMutex object.
+    The wrapper is designed to emulate Python's own threading.RLock
+    object. """
+
+    def __init__(self, name = "PythonRLock"):
+        pm.ReMutex.__init__(self, name)
+
+    def acquire(self, blocking = True):
+        if blocking:
+            pm.ReMutex.acquire(self)
+            return True
+        else:
+            return pm.ReMutex.tryAcquire(self)
+    
+    __enter__ = acquire
+
+    def __exit__(self, t, v, tb):
+        self.release()
+
+
+class Condition(pm.ConditionVarFull):
+    """ This class provides a wrapper around Panda's ConditionVarFull
+    object.  The wrapper is designed to emulate Python's own
+    threading.Condition object. """
+
+    def __init__(self, lock = None):
+        if not lock:
+            lock = Lock()
+
+        # Panda doesn't support RLock objects used with condition
+        # variables.
+        assert isinstance(lock, Lock)
+
+        self.__lock = lock
+        pm.ConditionVarFull.__init__(self, self.__lock)
+
+    def acquire(self, *args, **kw):
+        return self.__lock.acquire(*args, **kw)
+
+    def release(self):
+        self.__lock.release()
+
+    def wait(self, timeout = None):
+        if timeout is None:
+            pm.ConditionVarFull.wait(self)
+        else:
+            pm.ConditionVarFull.wait(self, timeout)
+
+    def notifyAll(self):
+        self.__cvar.notifyAll()
+
+    notify_all = notifyAll
+    
+    __enter__ = acquire
+
+    def __exit__(self, t, v, tb):
+        self.release()
+
+class Semaphore(pm.Semaphore):
+    """ This class provides a wrapper around Panda's Semaphore
+    object.  The wrapper is designed to emulate Python's own
+    threading.Semaphore object. """
+
+    def __init__(self, value = 1):
+        pm.Semaphore.__init__(self, value)
+
+    def acquire(self, blocking = True):
+        if blocking:
+            pm.Semaphore.acquire(self)
+            return True
+        else:
+            return pm.Semaphore.tryAcquire(self)
+    
+    __enter__ = acquire
+
+    def __exit__(self, t, v, tb):
+        self.release()
+
+class BoundedSemaphore(Semaphore):
+    """ This class provides a wrapper around Panda's Semaphore
+    object.  The wrapper is designed to emulate Python's own
+    threading.BoundedSemaphore object. """
+
+    def __init__(self, value = 1):
+        self.__max = value
+        Semaphore.__init__(value)
+
+    def release(self):
+        if self.getCount() > value:
+            raise ValueError
+
+        Semaphore.release(self)
+
+class Event:
+    """ This class is designed to emulate Python's own threading.Event
+    object. """
+
+    def __init__(self):
+        self.__lock = pm.Lock("Python Event")
+        self.__cvar = pm.ConditionVarFull(self.__lock)
+        self.__flag = False
+
+    def is_set(self):
+        return self.__flag
+
+    isSet = is_set
+
+    def set(self):
+        self.__lock.acquire()
+        try:
+            self.__flag = True
+            self.__cvar.signalAll()
+
+        finally:
+            self.__lock.release()
+            
+    def clear(self):
+        self.__lock.acquire()
+        try:
+            self.__flag = False
+
+        finally:
+            self.__lock.release()
+            
+    def wait(self, timeout = None):
+        self.__lock.acquire()
+        try:
+            if timeout is None:
+                while not self.__flag:
+                    self.__cvar.wait()
+            else:
+                clock = pm.TrueClock.getGlobalPtr()
+                expires = clock.getShortTime() + timeout
+                while not self.__flag:
+                    wait = expires - clock.getShortTime()
+                    if wait < 0:
+                        return
+                    
+                    self.__cvar.wait(wait)
+                
+        finally:
+            self.__lock.release()
+
+class Timer(Thread):
+    """Call a function after a specified number of seconds:
+
+    t = Timer(30.0, f, args=[], kwargs={})
+    t.start()
+    t.cancel() # stop the timer's action if it's still waiting
+    """
+
+    def __init__(self, interval, function, args=[], kwargs={}):
+        Thread.__init__(self)
+        self.interval = interval
+        self.function = function
+        self.args = args
+        self.kwargs = kwargs
+        self.finished = Event()
+
+    def cancel(self):
+        """Stop the timer if it hasn't finished yet"""
+        self.finished.set()
+
+    def run(self):
+        self.finished.wait(self.interval)
+        if not self.finished.isSet():
+            self.function(*self.args, **self.kwargs)
+        self.finished.set()
+
+def _create_thread_wrapper(t, threadId):
+    """ Creates a thread wrapper for the indicated external thread. """
+    if isinstance(t, pm.MainThread):
+        pyt = MainThread(t, threadId)
+    else:
+        pyt = ExternalThread(t, threadId)
+
+    return pyt
+
+def current_thread():
+    t = pm.Thread.getCurrentThread()
+    return _thread._get_thread_wrapper(t, _create_thread_wrapper)
+
+currentThread = current_thread
+
+def enumerate():
+    tlist = []
+    _thread._threadsLock.acquire()
+    try:
+        for thread, locals, wrapper in _thread._threads.values():
+            if wrapper and thread.isStarted():
+                tlist.append(wrapper)
+        return tlist
+    finally:
+        _thread._threadsLock.release()
+
+def active_count():
+    return len(enumerate())
+activeCount = active_count
+
+_settrace_func = None
+def settrace(func):
+    global _settrace_func
+    _settrace_func = func
+
+_setprofile_func = None
+def setprofile(func):
+    global _setprofile_func
+    _setprofile_func = func
+
+def stack_size(size = None):
+    raise ThreadError
+
+def _test():
+
+    from collections import deque
+    _sleep = pm.Thread.sleep
+
+    _VERBOSE = False
+
+    class _Verbose(object):
+
+        def __init__(self, verbose=None):
+            if verbose is None:
+                verbose = _VERBOSE
+            self.__verbose = verbose
+
+        def _note(self, format, *args):
+            if self.__verbose:
+                format = format % args
+                format = "%s: %s\n" % (
+                    currentThread().getName(), format)
+                _sys.stderr.write(format)
+
+    class BoundedQueue(_Verbose):
+
+        def __init__(self, limit):
+            _Verbose.__init__(self)
+            self.mon = Lock(name = "BoundedQueue.mon")
+            self.rc = Condition(self.mon)
+            self.wc = Condition(self.mon)
+            self.limit = limit
+            self.queue = deque()
+
+        def put(self, item):
+            self.mon.acquire()
+            while len(self.queue) >= self.limit:
+                self._note("put(%s): queue full", item)
+                self.wc.wait()
+            self.queue.append(item)
+            self._note("put(%s): appended, length now %d",
+                       item, len(self.queue))
+            self.rc.notify()
+            self.mon.release()
+
+        def get(self):
+            self.mon.acquire()
+            while not self.queue:
+                self._note("get(): queue empty")
+                self.rc.wait()
+            item = self.queue.popleft()
+            self._note("get(): got %s, %d left", item, len(self.queue))
+            self.wc.notify()
+            self.mon.release()
+            return item
+
+    class ProducerThread(Thread):
+
+        def __init__(self, queue, quota):
+            Thread.__init__(self, name="Producer")
+            self.queue = queue
+            self.quota = quota
+
+        def run(self):
+            from random import random
+            counter = 0
+            while counter < self.quota:
+                counter = counter + 1
+                self.queue.put("%s.%d" % (self.getName(), counter))
+                _sleep(random() * 0.00001)
+
+
+    class ConsumerThread(Thread):
+
+        def __init__(self, queue, count):
+            Thread.__init__(self, name="Consumer")
+            self.queue = queue
+            self.count = count
+
+        def run(self):
+            while self.count > 0:
+                item = self.queue.get()
+                print item
+                self.count = self.count - 1
+
+    NP = 3
+    QL = 4
+    NI = 5
+
+    Q = BoundedQueue(QL)
+    P = []
+    for i in range(NP):
+        t = ProducerThread(Q, NI)
+        t.setName("Producer-%d" % (i+1))
+        P.append(t)
+    C = ConsumerThread(Q, NI*NP)
+    for t in P:
+        t.start()
+        _sleep(0.000001)
+    C.start()
+    for t in P:
+        t.join()
+    C.join()
+
+if __name__ == '__main__':
+    _test()

+ 828 - 0
direct/src/stdpy/threading2.py

@@ -0,0 +1,828 @@
+""" This module reimplements Python's native threading module using Panda
+threading constructs.  It's designed as a drop-in replacement for the
+threading module for code that works with Panda; it is necessary because
+in some compilation models, Panda's threading constructs are
+incompatible with the OS-provided threads used by Python's thread
+module.
+
+Unlike threading.py, this module is a more explicit implementation of
+Python's threading model, designed to more precisely emulate Python's
+standard threading semantics.  In fact, this is a bald-face copy of
+Python's threading module from Python 2.5, with a few lines at the top
+to import Panda's thread reimplementation instead of the system thread
+module, and so it is therefore layered on top of Panda's thread
+implementation. """
+
+import sys as _sys
+
+from direct.stdpy import thread
+from direct.stdpy.thread import stack_size, _local as local
+from pandac import PandaModules as pm
+_sleep = pm.Thread.sleep
+
+from time import time as _time
+from traceback import format_exc as _format_exc
+from collections import deque
+
+# Rename some stuff so "from threading import *" is safe
+__all__ = ['activeCount', 'Condition', 'currentThread', 'enumerate', 'Event',
+           'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore', 'Thread',
+           'Timer', 'setprofile', 'settrace', 'local', 'stack_size']
+
+_start_new_thread = thread.start_new_thread
+_allocate_lock = thread.allocate_lock
+_get_ident = thread.get_ident
+ThreadError = thread.error
+del thread
+
+
+# Debug support (adapted from ihooks.py).
+# All the major classes here derive from _Verbose.  We force that to
+# be a new-style class so that all the major classes here are new-style.
+# This helps debugging (type(instance) is more revealing for instances
+# of new-style classes).
+
+_VERBOSE = False
+
+if __debug__:
+
+    class _Verbose(object):
+
+        def __init__(self, verbose=None):
+            if verbose is None:
+                verbose = _VERBOSE
+            self.__verbose = verbose
+
+        def _note(self, format, *args):
+            if self.__verbose:
+                format = format % args
+                format = "%s: %s\n" % (
+                    currentThread().getName(), format)
+                _sys.stderr.write(format)
+
+else:
+    # Disable this when using "python -O"
+    class _Verbose(object):
+        def __init__(self, verbose=None):
+            pass
+        def _note(self, *args):
+            pass
+
+# Support for profile and trace hooks
+
+_profile_hook = None
+_trace_hook = None
+
+def setprofile(func):
+    global _profile_hook
+    _profile_hook = func
+
+def settrace(func):
+    global _trace_hook
+    _trace_hook = func
+
+# Synchronization classes
+
+Lock = _allocate_lock
+
+def RLock(*args, **kwargs):
+    return _RLock(*args, **kwargs)
+
+class _RLock(_Verbose):
+
+    def __init__(self, verbose=None):
+        _Verbose.__init__(self, verbose)
+        self.__block = _allocate_lock()
+        self.__owner = None
+        self.__count = 0
+
+    def __repr__(self):
+        return "<%s(%s, %d)>" % (
+                self.__class__.__name__,
+                self.__owner and self.__owner.getName(),
+                self.__count)
+
+    def acquire(self, blocking=1):
+        me = currentThread()
+        if self.__owner is me:
+            self.__count = self.__count + 1
+            if __debug__:
+                self._note("%s.acquire(%s): recursive success", self, blocking)
+            return 1
+        rc = self.__block.acquire(blocking)
+        if rc:
+            self.__owner = me
+            self.__count = 1
+            if __debug__:
+                self._note("%s.acquire(%s): initial success", self, blocking)
+        else:
+            if __debug__:
+                self._note("%s.acquire(%s): failure", self, blocking)
+        return rc
+
+    __enter__ = acquire
+
+    def release(self):
+        me = currentThread()
+        assert self.__owner is me, "release() of un-acquire()d lock"
+        self.__count = count = self.__count - 1
+        if not count:
+            self.__owner = None
+            self.__block.release()
+            if __debug__:
+                self._note("%s.release(): final release", self)
+        else:
+            if __debug__:
+                self._note("%s.release(): non-final release", self)
+
+    def __exit__(self, t, v, tb):
+        self.release()
+
+    # Internal methods used by condition variables
+
+    def _acquire_restore(self, (count, owner)):
+        self.__block.acquire()
+        self.__count = count
+        self.__owner = owner
+        if __debug__:
+            self._note("%s._acquire_restore()", self)
+
+    def _release_save(self):
+        if __debug__:
+            self._note("%s._release_save()", self)
+        count = self.__count
+        self.__count = 0
+        owner = self.__owner
+        self.__owner = None
+        self.__block.release()
+        return (count, owner)
+
+    def _is_owned(self):
+        return self.__owner is currentThread()
+
+
+def Condition(*args, **kwargs):
+    return _Condition(*args, **kwargs)
+
+class _Condition(_Verbose):
+
+    def __init__(self, lock=None, verbose=None):
+        _Verbose.__init__(self, verbose)
+        if lock is None:
+            lock = RLock()
+        self.__lock = lock
+        # Export the lock's acquire() and release() methods
+        self.acquire = lock.acquire
+        self.release = lock.release
+        # If the lock defines _release_save() and/or _acquire_restore(),
+        # these override the default implementations (which just call
+        # release() and acquire() on the lock).  Ditto for _is_owned().
+        try:
+            self._release_save = lock._release_save
+        except AttributeError:
+            pass
+        try:
+            self._acquire_restore = lock._acquire_restore
+        except AttributeError:
+            pass
+        try:
+            self._is_owned = lock._is_owned
+        except AttributeError:
+            pass
+        self.__waiters = []
+
+    def __enter__(self):
+        return self.__lock.__enter__()
+
+    def __exit__(self, *args):
+        return self.__lock.__exit__(*args)
+
+    def __repr__(self):
+        return "<Condition(%s, %d)>" % (self.__lock, len(self.__waiters))
+
+    def _release_save(self):
+        self.__lock.release()           # No state to save
+
+    def _acquire_restore(self, x):
+        self.__lock.acquire()           # Ignore saved state
+
+    def _is_owned(self):
+        # Return True if lock is owned by currentThread.
+        # This method is called only if __lock doesn't have _is_owned().
+        if self.__lock.acquire(0):
+            self.__lock.release()
+            return False
+        else:
+            return True
+
+    def wait(self, timeout=None):
+        assert self._is_owned(), "wait() of un-acquire()d lock"
+        waiter = _allocate_lock()
+        waiter.acquire()
+        self.__waiters.append(waiter)
+        saved_state = self._release_save()
+        try:    # restore state no matter what (e.g., KeyboardInterrupt)
+            if timeout is None:
+                waiter.acquire()
+                if __debug__:
+                    self._note("%s.wait(): got it", self)
+            else:
+                # Balancing act:  We can't afford a pure busy loop, so we
+                # have to sleep; but if we sleep the whole timeout time,
+                # we'll be unresponsive.  The scheme here sleeps very
+                # little at first, longer as time goes on, but never longer
+                # than 20 times per second (or the timeout time remaining).
+                endtime = _time() + timeout
+                delay = 0.0005 # 500 us -> initial delay of 1 ms
+                while True:
+                    gotit = waiter.acquire(0)
+                    if gotit:
+                        break
+                    remaining = endtime - _time()
+                    if remaining <= 0:
+                        break
+                    delay = min(delay * 2, remaining, .05)
+                    _sleep(delay)
+                if not gotit:
+                    if __debug__:
+                        self._note("%s.wait(%s): timed out", self, timeout)
+                    try:
+                        self.__waiters.remove(waiter)
+                    except ValueError:
+                        pass
+                else:
+                    if __debug__:
+                        self._note("%s.wait(%s): got it", self, timeout)
+        finally:
+            self._acquire_restore(saved_state)
+
+    def notify(self, n=1):
+        assert self._is_owned(), "notify() of un-acquire()d lock"
+        __waiters = self.__waiters
+        waiters = __waiters[:n]
+        if not waiters:
+            if __debug__:
+                self._note("%s.notify(): no waiters", self)
+            return
+        self._note("%s.notify(): notifying %d waiter%s", self, n,
+                   n!=1 and "s" or "")
+        for waiter in waiters:
+            waiter.release()
+            try:
+                __waiters.remove(waiter)
+            except ValueError:
+                pass
+
+    def notifyAll(self):
+        self.notify(len(self.__waiters))
+
+
+def Semaphore(*args, **kwargs):
+    return _Semaphore(*args, **kwargs)
+
+class _Semaphore(_Verbose):
+
+    # After Tim Peters' semaphore class, but not quite the same (no maximum)
+
+    def __init__(self, value=1, verbose=None):
+        assert value >= 0, "Semaphore initial value must be >= 0"
+        _Verbose.__init__(self, verbose)
+        self.__cond = Condition(Lock())
+        self.__value = value
+
+    def acquire(self, blocking=1):
+        rc = False
+        self.__cond.acquire()
+        while self.__value == 0:
+            if not blocking:
+                break
+            if __debug__:
+                self._note("%s.acquire(%s): blocked waiting, value=%s",
+                           self, blocking, self.__value)
+            self.__cond.wait()
+        else:
+            self.__value = self.__value - 1
+            if __debug__:
+                self._note("%s.acquire: success, value=%s",
+                           self, self.__value)
+            rc = True
+        self.__cond.release()
+        return rc
+
+    __enter__ = acquire
+
+    def release(self):
+        self.__cond.acquire()
+        self.__value = self.__value + 1
+        if __debug__:
+            self._note("%s.release: success, value=%s",
+                       self, self.__value)
+        self.__cond.notify()
+        self.__cond.release()
+
+    def __exit__(self, t, v, tb):
+        self.release()
+
+
+def BoundedSemaphore(*args, **kwargs):
+    return _BoundedSemaphore(*args, **kwargs)
+
+class _BoundedSemaphore(_Semaphore):
+    """Semaphore that checks that # releases is <= # acquires"""
+    def __init__(self, value=1, verbose=None):
+        _Semaphore.__init__(self, value, verbose)
+        self._initial_value = value
+
+    def release(self):
+        if self._Semaphore__value >= self._initial_value:
+            raise ValueError, "Semaphore released too many times"
+        return _Semaphore.release(self)
+
+
+def Event(*args, **kwargs):
+    return _Event(*args, **kwargs)
+
+class _Event(_Verbose):
+
+    # After Tim Peters' event class (without is_posted())
+
+    def __init__(self, verbose=None):
+        _Verbose.__init__(self, verbose)
+        self.__cond = Condition(Lock())
+        self.__flag = False
+
+    def isSet(self):
+        return self.__flag
+
+    def set(self):
+        self.__cond.acquire()
+        try:
+            self.__flag = True
+            self.__cond.notifyAll()
+        finally:
+            self.__cond.release()
+
+    def clear(self):
+        self.__cond.acquire()
+        try:
+            self.__flag = False
+        finally:
+            self.__cond.release()
+
+    def wait(self, timeout=None):
+        self.__cond.acquire()
+        try:
+            if not self.__flag:
+                self.__cond.wait(timeout)
+        finally:
+            self.__cond.release()
+
+# Helper to generate new thread names
+_counter = 0
+def _newname(template="Thread-%d"):
+    global _counter
+    _counter = _counter + 1
+    return template % _counter
+
+# Active thread administration
+_active_limbo_lock = _allocate_lock()
+_active = {}    # maps thread id to Thread object
+_limbo = {}
+
+
+# Main class for threads
+
+class Thread(_Verbose):
+
+    __initialized = False
+    # Need to store a reference to sys.exc_info for printing
+    # out exceptions when a thread tries to use a global var. during interp.
+    # shutdown and thus raises an exception about trying to perform some
+    # operation on/with a NoneType
+    __exc_info = _sys.exc_info
+
+    def __init__(self, group=None, target=None, name=None,
+                 args=(), kwargs=None, verbose=None):
+        assert group is None, "group argument must be None for now"
+        _Verbose.__init__(self, verbose)
+        if kwargs is None:
+            kwargs = {}
+        self.__target = target
+        self.__name = str(name or _newname())
+        self.__args = args
+        self.__kwargs = kwargs
+        self.__daemonic = self._set_daemon()
+        self.__started = False
+        self.__stopped = False
+        self.__block = Condition(Lock())
+        self.__initialized = True
+        # sys.stderr is not stored in the class like
+        # sys.exc_info since it can be changed between instances
+        self.__stderr = _sys.stderr
+
+    def _set_daemon(self):
+        # Overridden in _MainThread and _DummyThread
+        return currentThread().isDaemon()
+
+    def __repr__(self):
+        assert self.__initialized, "Thread.__init__() was not called"
+        status = "initial"
+        if self.__started:
+            status = "started"
+        if self.__stopped:
+            status = "stopped"
+        if self.__daemonic:
+            status = status + " daemon"
+        return "<%s(%s, %s)>" % (self.__class__.__name__, self.__name, status)
+
+    def start(self):
+        assert self.__initialized, "Thread.__init__() not called"
+        assert not self.__started, "thread already started"
+        if __debug__:
+            self._note("%s.start(): starting thread", self)
+        _active_limbo_lock.acquire()
+        _limbo[self] = self
+        _active_limbo_lock.release()
+        _start_new_thread(self.__bootstrap, ())
+        self.__started = True
+        _sleep(0.000001)    # 1 usec, to let the thread run (Solaris hack)
+
+    def run(self):
+        if self.__target:
+            self.__target(*self.__args, **self.__kwargs)
+
+    def __bootstrap(self):
+        try:
+            self.__started = True
+            _active_limbo_lock.acquire()
+            _active[_get_ident()] = self
+            del _limbo[self]
+            _active_limbo_lock.release()
+            if __debug__:
+                self._note("%s.__bootstrap(): thread started", self)
+
+            if _trace_hook:
+                self._note("%s.__bootstrap(): registering trace hook", self)
+                _sys.settrace(_trace_hook)
+            if _profile_hook:
+                self._note("%s.__bootstrap(): registering profile hook", self)
+                _sys.setprofile(_profile_hook)
+
+            try:
+                self.run()
+            except SystemExit:
+                if __debug__:
+                    self._note("%s.__bootstrap(): raised SystemExit", self)
+            except:
+                if __debug__:
+                    self._note("%s.__bootstrap(): unhandled exception", self)
+                # If sys.stderr is no more (most likely from interpreter
+                # shutdown) use self.__stderr.  Otherwise still use sys (as in
+                # _sys) in case sys.stderr was redefined since the creation of
+                # self.
+                if _sys:
+                    _sys.stderr.write("Exception in thread %s:\n%s\n" %
+                                      (self.getName(), _format_exc()))
+                else:
+                    # Do the best job possible w/o a huge amt. of code to
+                    # approximate a traceback (code ideas from
+                    # Lib/traceback.py)
+                    exc_type, exc_value, exc_tb = self.__exc_info()
+                    try:
+                        print>>self.__stderr, (
+                            "Exception in thread " + self.getName() +
+                            " (most likely raised during interpreter shutdown):")
+                        print>>self.__stderr, (
+                            "Traceback (most recent call last):")
+                        while exc_tb:
+                            print>>self.__stderr, (
+                                '  File "%s", line %s, in %s' %
+                                (exc_tb.tb_frame.f_code.co_filename,
+                                    exc_tb.tb_lineno,
+                                    exc_tb.tb_frame.f_code.co_name))
+                            exc_tb = exc_tb.tb_next
+                        print>>self.__stderr, ("%s: %s" % (exc_type, exc_value))
+                    # Make sure that exc_tb gets deleted since it is a memory
+                    # hog; deleting everything else is just for thoroughness
+                    finally:
+                        del exc_type, exc_value, exc_tb
+            else:
+                if __debug__:
+                    self._note("%s.__bootstrap(): normal return", self)
+        finally:
+            self.__stop()
+            try:
+                self.__delete()
+            except:
+                pass
+
+    def __stop(self):
+        self.__block.acquire()
+        self.__stopped = True
+        self.__block.notifyAll()
+        self.__block.release()
+
+    def __delete(self):
+        "Remove current thread from the dict of currently running threads."
+
+        # Notes about running with dummy_thread:
+        #
+        # Must take care to not raise an exception if dummy_thread is being
+        # used (and thus this module is being used as an instance of
+        # dummy_threading).  dummy_thread.get_ident() always returns -1 since
+        # there is only one thread if dummy_thread is being used.  Thus
+        # len(_active) is always <= 1 here, and any Thread instance created
+        # overwrites the (if any) thread currently registered in _active.
+        #
+        # An instance of _MainThread is always created by 'threading'.  This
+        # gets overwritten the instant an instance of Thread is created; both
+        # threads return -1 from dummy_thread.get_ident() and thus have the
+        # same key in the dict.  So when the _MainThread instance created by
+        # 'threading' tries to clean itself up when atexit calls this method
+        # it gets a KeyError if another Thread instance was created.
+        #
+        # This all means that KeyError from trying to delete something from
+        # _active if dummy_threading is being used is a red herring.  But
+        # since it isn't if dummy_threading is *not* being used then don't
+        # hide the exception.
+
+        _active_limbo_lock.acquire()
+        try:
+            try:
+                del _active[_get_ident()]
+            except KeyError:
+                if 'dummy_threading' not in _sys.modules:
+                    raise
+        finally:
+            _active_limbo_lock.release()
+
+    def join(self, timeout=None):
+        assert self.__initialized, "Thread.__init__() not called"
+        assert self.__started, "cannot join thread before it is started"
+        assert self is not currentThread(), "cannot join current thread"
+        if __debug__:
+            if not self.__stopped:
+                self._note("%s.join(): waiting until thread stops", self)
+        self.__block.acquire()
+        try:
+            if timeout is None:
+                while not self.__stopped:
+                    self.__block.wait()
+                if __debug__:
+                    self._note("%s.join(): thread stopped", self)
+            else:
+                deadline = _time() + timeout
+                while not self.__stopped:
+                    delay = deadline - _time()
+                    if delay <= 0:
+                        if __debug__:
+                            self._note("%s.join(): timed out", self)
+                        break
+                    self.__block.wait(delay)
+                else:
+                    if __debug__:
+                        self._note("%s.join(): thread stopped", self)
+        finally:
+            self.__block.release()
+
+    def getName(self):
+        assert self.__initialized, "Thread.__init__() not called"
+        return self.__name
+
+    def setName(self, name):
+        assert self.__initialized, "Thread.__init__() not called"
+        self.__name = str(name)
+
+    def isAlive(self):
+        assert self.__initialized, "Thread.__init__() not called"
+        return self.__started and not self.__stopped
+
+    def isDaemon(self):
+        assert self.__initialized, "Thread.__init__() not called"
+        return self.__daemonic
+
+    def setDaemon(self, daemonic):
+        assert self.__initialized, "Thread.__init__() not called"
+        assert not self.__started, "cannot set daemon status of active thread"
+        self.__daemonic = daemonic
+
+# The timer class was contributed by Itamar Shtull-Trauring
+
+def Timer(*args, **kwargs):
+    return _Timer(*args, **kwargs)
+
+class _Timer(Thread):
+    """Call a function after a specified number of seconds:
+
+    t = Timer(30.0, f, args=[], kwargs={})
+    t.start()
+    t.cancel() # stop the timer's action if it's still waiting
+    """
+
+    def __init__(self, interval, function, args=[], kwargs={}):
+        Thread.__init__(self)
+        self.interval = interval
+        self.function = function
+        self.args = args
+        self.kwargs = kwargs
+        self.finished = Event()
+
+    def cancel(self):
+        """Stop the timer if it hasn't finished yet"""
+        self.finished.set()
+
+    def run(self):
+        self.finished.wait(self.interval)
+        if not self.finished.isSet():
+            self.function(*self.args, **self.kwargs)
+        self.finished.set()
+
+# Special thread class to represent the main thread
+# This is garbage collected through an exit handler
+
+class _MainThread(Thread):
+
+    def __init__(self):
+        Thread.__init__(self, name="MainThread")
+        self._Thread__started = True
+        _active_limbo_lock.acquire()
+        _active[_get_ident()] = self
+        _active_limbo_lock.release()
+
+    def _set_daemon(self):
+        return False
+
+    def _exitfunc(self):
+        self._Thread__stop()
+        t = _pickSomeNonDaemonThread()
+        if t:
+            if __debug__:
+                self._note("%s: waiting for other threads", self)
+        while t:
+            t.join()
+            t = _pickSomeNonDaemonThread()
+        if __debug__:
+            self._note("%s: exiting", self)
+        self._Thread__delete()
+
+def _pickSomeNonDaemonThread():
+    for t in enumerate():
+        if not t.isDaemon() and t.isAlive():
+            return t
+    return None
+
+
+# Dummy thread class to represent threads not started here.
+# These aren't garbage collected when they die, nor can they be waited for.
+# If they invoke anything in threading.py that calls currentThread(), they
+# leave an entry in the _active dict forever after.
+# Their purpose is to return *something* from currentThread().
+# They are marked as daemon threads so we won't wait for them
+# when we exit (conform previous semantics).
+
+class _DummyThread(Thread):
+
+    def __init__(self):
+        Thread.__init__(self, name=_newname("Dummy-%d"))
+
+        # Thread.__block consumes an OS-level locking primitive, which
+        # can never be used by a _DummyThread.  Since a _DummyThread
+        # instance is immortal, that's bad, so release this resource.
+        del self._Thread__block
+
+        self._Thread__started = True
+        _active_limbo_lock.acquire()
+        _active[_get_ident()] = self
+        _active_limbo_lock.release()
+
+    def _set_daemon(self):
+        return True
+
+    def join(self, timeout=None):
+        assert False, "cannot join a dummy thread"
+
+
+# Global API functions
+
+def currentThread():
+    try:
+        return _active[_get_ident()]
+    except KeyError:
+        ##print "currentThread(): no current thread for", _get_ident()
+        return _DummyThread()
+
+def activeCount():
+    _active_limbo_lock.acquire()
+    count = len(_active) + len(_limbo)
+    _active_limbo_lock.release()
+    return count
+
+def enumerate():
+    _active_limbo_lock.acquire()
+    active = _active.values() + _limbo.values()
+    _active_limbo_lock.release()
+    return active
+
+#from thread import stack_size
+
+# Create the main thread object,
+# and make it available for the interpreter
+# (Py_Main) as threading._shutdown.
+
+_shutdown = _MainThread()._exitfunc
+
+# get thread-local implementation, either from the thread
+# module, or from the python fallback
+
+## try:
+##     from thread import _local as local
+## except ImportError:
+##     from _threading_local import local
+
+
+# Self-test code
+
+def _test():
+
+    class BoundedQueue(_Verbose):
+
+        def __init__(self, limit):
+            _Verbose.__init__(self)
+            self.mon = RLock()
+            self.rc = Condition(self.mon)
+            self.wc = Condition(self.mon)
+            self.limit = limit
+            self.queue = deque()
+
+        def put(self, item):
+            self.mon.acquire()
+            while len(self.queue) >= self.limit:
+                self._note("put(%s): queue full", item)
+                self.wc.wait()
+            self.queue.append(item)
+            self._note("put(%s): appended, length now %d",
+                       item, len(self.queue))
+            self.rc.notify()
+            self.mon.release()
+
+        def get(self):
+            self.mon.acquire()
+            while not self.queue:
+                self._note("get(): queue empty")
+                self.rc.wait()
+            item = self.queue.popleft()
+            self._note("get(): got %s, %d left", item, len(self.queue))
+            self.wc.notify()
+            self.mon.release()
+            return item
+
+    class ProducerThread(Thread):
+
+        def __init__(self, queue, quota):
+            Thread.__init__(self, name="Producer")
+            self.queue = queue
+            self.quota = quota
+
+        def run(self):
+            from random import random
+            counter = 0
+            while counter < self.quota:
+                counter = counter + 1
+                self.queue.put("%s.%d" % (self.getName(), counter))
+                _sleep(random() * 0.00001)
+
+
+    class ConsumerThread(Thread):
+
+        def __init__(self, queue, count):
+            Thread.__init__(self, name="Consumer")
+            self.queue = queue
+            self.count = count
+
+        def run(self):
+            while self.count > 0:
+                item = self.queue.get()
+                print item
+                self.count = self.count - 1
+
+    NP = 3
+    QL = 4
+    NI = 5
+
+    Q = BoundedQueue(QL)
+    P = []
+    for i in range(NP):
+        t = ProducerThread(Q, NI)
+        t.setName("Producer-%d" % (i+1))
+        P.append(t)
+    C = ConsumerThread(Q, NI*NP)
+    for t in P:
+        t.start()
+        _sleep(0.000001)
+    C.start()
+    for t in P:
+        t.join()
+    C.join()
+
+if __name__ == '__main__':
+    _test()