diff options
author | Guido van Rossum <guido@python.org> | 1998-04-09 22:01:42 (GMT) |
---|---|---|
committer | Guido van Rossum <guido@python.org> | 1998-04-09 22:01:42 (GMT) |
commit | 7f5013a9a9678e86bff00c97f98e7a92c515b94d (patch) | |
tree | c966cc1b9dc9d9c255c49df0f0dd80a5fe0e23b0 | |
parent | bb08066053c51de274208eea645a0d9a470c87f9 (diff) | |
download | cpython-7f5013a9a9678e86bff00c97f98e7a92c515b94d.zip cpython-7f5013a9a9678e86bff00c97f98e7a92c515b94d.tar.gz cpython-7f5013a9a9678e86bff00c97f98e7a92c515b94d.tar.bz2 |
New Java-style threading module. The doc strings are in a separate module.
-rw-r--r-- | Lib/threading.py | 638 | ||||
-rw-r--r-- | Lib/threading_api.py | 638 |
2 files changed, 1276 insertions, 0 deletions
diff --git a/Lib/threading.py b/Lib/threading.py new file mode 100644 index 0000000..79319c1 --- /dev/null +++ b/Lib/threading.py @@ -0,0 +1,638 @@ +# threading.py: +# Proposed new threading module, emulating a subset of Java's threading model + +import sys +import time +import thread +import traceback +import StringIO + +# Rename some stuff so "from threading import *" is safe + +_sys = sys +del sys + +_time = time.time +_sleep = time.sleep +del time + +_start_new_thread = thread.start_new_thread +_allocate_lock = thread.allocate_lock +_get_ident = thread.get_ident +del thread + +_print_exc = traceback.print_exc +del traceback + +_StringIO = StringIO.StringIO +del StringIO + + +# Debug support (adapted from ihooks.py) + +_VERBOSE = 0 + +if __debug__: + + class _Verbose: + + 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: + def __init__(self, verbose=None): + pass + def _note(self, *args): + pass + + +# Synchronization classes + +Lock = _allocate_lock + +def RLock(*args, **kwargs): + return apply(_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 succes", self, blocking) + else: + if __debug__: + self._note("%s.acquire(%s): failure", self, blocking) + return rc + + 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) + + # 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 apply(_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 __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): + if self.__lock.acquire(0): + self.__lock.release() + return 0 + else: + return 1 + + def wait(self, timeout=None): + me = currentThread() + assert self._is_owned(), "wait() of un-acquire()d lock" + waiter = _allocate_lock() + waiter.acquire() + self.__waiters.append(waiter) + saved_state = self._release_save() + if timeout is None: + waiter.acquire() + if __debug__: + self._note("%s.wait(): got it", self) + else: + endtime = _time() + timeout + delay = 0.000001 # 1 usec + while 1: + gotit = waiter.acquire(0) + if gotit or _time() >= endtime: + break + _sleep(delay) + if delay < 1.0: + delay = delay * 2.0 + 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) + self._acquire_restore(saved_state) + + def notify(self, n=1): + me = currentThread() + 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 apply(_Semaphore, args, kwargs) + +class _Semaphore(_Verbose): + + # After Tim Peters' semaphore class, but bnot 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 = 0 + self.__cond.acquire() + while self.__value == 0: + if not blocking: + break + self.__cond.wait() + else: + self.__value = self.__value - 1 + rc = 1 + self.__cond.release() + return rc + + def release(self): + self.__cond.acquire() + self.__value = self.__value + 1 + self.__cond.notify() + self.__cond.release() + + +def Event(*args, **kwargs): + return apply(_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 = 0 + + def isSet(self): + return self.__flag + + def set(self): + self.__cond.acquire() + self.__flag = 1 + self.__cond.notifyAll() + self.__cond.release() + + def clear(self): + self.__cond.acquire() + self.__flag = 0 + self.__cond.release() + + def wait(self, timeout=None): + self.__cond.acquire() + if not self.__flag: + self.__cond.wait(timeout) + 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 = {} +_limbo = {} + + +# Main class for threads + +class Thread(_Verbose): + + __initialized = 0 + + def __init__(self, group=None, target=None, name=None, + args=(), kwargs={}, verbose=None): + assert group is None, "group argument must be None for now" + _Verbose.__init__(self, verbose) + self.__target = target + self.__name = str(name or _newname()) + self.__args = args + self.__kwargs = kwargs + self.__daemonic = self._set_daemon() + self.__started = 0 + self.__stopped = 0 + self.__block = Condition(Lock()) + self.__initialized = 1 + + 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 = 1 + _sleep(0.000001) # 1 usec, to let the thread run (Solaris hack) + + def run(self): + if self.__target: + apply(self.__target, self.__args, self.__kwargs) + + def __bootstrap(self): + try: + self.__started = 1 + _active_limbo_lock.acquire() + _active[_get_ident()] = self + del _limbo[self] + _active_limbo_lock.release() + if __debug__: + self._note("%s.__bootstrap(): thread started", self) + try: + self.run() + except SystemExit: + if __debug__: + self._note("%s.__bootstrap(): raised SystemExit", self) + except: + if __debug__: + self._note("%s.__bootstrap(): unhandled exception", self) + s = _StringIO() + _print_exc(file=s) + _sys.stderr.write("Exception in thread %s:\n%s\n" % + (self.getName(), s.getvalue())) + else: + if __debug__: + self._note("%s.__bootstrap(): normal return", self) + finally: + self.__stop() + self.__delete() + + def __stop(self): + self.__block.acquire() + self.__stopped = 1 + self.__block.notifyAll() + self.__block.release() + + def __delete(self): + _active_limbo_lock.acquire() + del _active[_get_ident()] + _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() + if timeout is None: + while not self.__stopped: + self.__block.wait() + if __debug__: + self._note("%s.join(): thread stopped", self) + else: + deadline = time.time() + timeout + while not self.__stopped: + delay = deadline - time.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) + 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 + + +# 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 = 1 + _active_limbo_lock.acquire() + _active[_get_ident()] = self + _active_limbo_lock.release() + try: + self.__oldexitfunc = _sys.exitfunc + except AttributeError: + self.__oldexitfunc = None + _sys.exitfunc = self.__exitfunc + + def _set_daemon(self): + return 0 + + 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 self.__oldexitfunc: + if __debug__: + self._note("%s: calling exit handler", self) + self.__oldexitfunc() + 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. +# 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")) + self.__Thread_started = 1 + _active_limbo_lock.acquire() + _active[_get_ident()] = self + _active_limbo_lock.release() + + def _set_daemon(self): + return 1 + + def join(self): + assert 0, "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 + + +# Create the main thread object + +_MainThread() + + +# Self-test code + +def _test(): + + import whrandom + + 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 = [] + + 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[0] + del self.queue[0] + 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 whrandom 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 + + import time + + 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() diff --git a/Lib/threading_api.py b/Lib/threading_api.py new file mode 100644 index 0000000..285cee1 --- /dev/null +++ b/Lib/threading_api.py @@ -0,0 +1,638 @@ +"""Proposed new higher-level threading interfaces. + +This module is safe for use with 'from threading import *'. It +defines the following objects: + +Lock() + A factory function that returns a new primitive lock object. Once + a thread has acquired it, subsequent attempts to acquire it block, + until it is released; any thread may release it. + +RLock() + A factory function that returns a new reentrant lock object. + A reentrant lock must be released by the thread that acquired it. + Once a thread has acquired a reentrant lock, the same thread may + acquire it again without blocking; the thread must release it once + for each time it has acquired it. + +Condition() + A factory function that returns a new condition variable object. + A condition variable allows one or more threads to wait until they + are notified by another thread. + +Semaphore() + A factory function that returns a new semaphore object. A + semaphore manages a counter representing the number of release() + calls minus the number of acquire() calls, plus an initial value. + The acquire() method blocks if necessary until it can return + without making the counter negative. + +Event() + A factory function that returns a new event object. An event + manages a flag that can be set to true with the set() method and + reset to false with the clear() method. The wait() method blocks + until the flag is true. + +Thread + A class that represents a thread of control -- subclassable. + +currentThread() + A function that returns the Thread object for the caller's thread. + +activeCount() + A function that returns the number of currently active threads. + +enumerate() + A function that returns a list of all currently active threads. + +Detailed interfaces for each of these are documented below in the form +of pseudo class definitions. Note that the classes marked as ``do not +subclass'' are actually implemented as factory functions; classes are +shown here as a way to structure the documentation only. + +The design of this module is loosely based on Java's threading model. +However, where Java makes locks and condition variables basic behavior +of every object, they are separate objects in Python. Python's Thread +class supports a subset of the behavior of Java's Thread class; +currently, there are no priorities, no thread groups, and threads +cannot be destroyed, stopped, suspended, resumed, or interrupted. The +static methods of Java's Thread class, when implemented, are mapped to +module-level functions. + +All methods described below are executed atomically. + +""" + + +class Lock: + """Primitive lock object. + + *** DO NOT SUBCLASS THIS CLASS *** + + A primitive lock is a synchronization primitive that is not owned + by a particular thread when locked. In Python, it is currently + the lowest level synchronization primitive available, implemented + directly by the thread extension module. + + A primitive lock is in one of two states, ``locked'' or + ``unlocked''. It is created in the unlocked state. It has two + basic methods, acquire() and release(). When the state is + unlocked, acquire() changes the state to locked and returns + immediately. When the state is locked, acquire() blocks until a + call to release() in another thread changes it to unlocked, then + the acquire() call resets it to locked and returns. The release() + method should only be called in the locked state; it changes the + state to unlocked and returns immediately. When more than one + thread is blocked in acquire() waiting for the state to turn to + unlocked, only one thread proceeds when a release() call resets + the state to unlocked; which one of the waiting threads proceeds + is not defined, and may vary across implementations. + + All methods are executed atomically. + + """ + + def acquire(self, blocking=1): + """Acquire a lock, blocking or non-blocking. + + When invoked without arguments, block until the lock is + unlocked, then set it to locked, and return. There is no + return value in this case. + + When invoked with the 'blocking' argument set to true, do the + same thing as when called without arguments, and return true. + + When invoked with the 'blocking' argument set to false, do not + block. If a call without argument would block, return false + immediately; otherwise, do the same thing as when called + without arguments, and return true. + + """ + + def release(self): + """Release a lock. + + When the lock is locked, reset it to unlocked, and return. If + any other threads are blocked waiting for the lock to become + unlocked, allow exactly one of them to proceed. + + Do not call this method when the lock is unlocked. + + There is no return value. + + """ + + +class RLock: + """Reentrant lock object. + + *** DO NOT SUBCLASS THIS CLASS *** + + A reentrant lock is a synchronization primitive that may be + acquired multiple times by the same thread. Internally, it uses + the concepts of ``owning thread'' and ``recursion level'' in + addition to the locked/unlocked state used by primitive locks. In + the locked state, some thread owns the lock; in the unlocked + state, no thread owns it. + + To lock the lock, a thread calls its acquire() method; this + returns once the thread owns the lock. To unlock the lock, a + thread calls its release() method. acquire()/release() call pairs + may be nested; only the final release() (i.e. the release() of the + outermost pair) resets the lock to unlocked and allows another + thread blocked in acquire() to proceed. + + """ + + def acquire(self, blocking=1): + """Acquire a lock, blocking or non-blocking. + + When invoked without arguments: if this thread already owns + the lock, increment the recursion level by one, and return + immediately. Otherwise, if another thread owns the lock, + block until the lock is unlocked. Once the lock is unlocked + (not owned by any thread), then grab ownership, set the + recursion level to one, and return. If more than one thread + is blocked waiting until the lock is unlocked, only one at a + time will be able to grab ownership of the lock. There is no + return value in this case. + + When invoked with the 'blocking' argument set to true, do the + same thing as when called without arguments, and return true. + + When invoked with the 'blocking' argument set to false, do not + block. If a call without argument would block, return false + immediately; otherwise, do the same thing as when called + without arguments, and return true. + + """ + + def release(self): + """Release a lock. + + Only call this method when the calling thread owns the lock. + Decrement the recursion level. If after the decrement it is + zero, reset the lock to unlocked (not owned by any thread), + and if any other threads are blocked waiting for the lock to + become unlocked, allow exactly one of them to proceed. If + after the decrement the recursion level is still nonzero, the + lock remains locked and owned by the calling thread. + + Do not call this method when the lock is unlocked. + + There is no return value. + + """ + + +class Condition: + """Synchronized condition variable object. + + *** DO NOT SUBCLASS THIS CLASS *** + + A condition variable is always associated with some kind of lock; + this can be passed in or one will be created by default. (Passing + one in is useful when several condition variables must share the + same lock.) + + A condition variable has acquire() and release() methods that call + the corresponding methods of the associated lock. + + It also has a wait() method, and notify() and notifyAll() methods. + These three must only be called when the calling thread has + acquired the lock. + + The wait() method releases the lock, and then blocks until it is + awakened by a notifiy() or notifyAll() call for the same condition + variable in another thread. Once awakened, it re-acquires the + lock and returns. It is also possible to specify a timeout. + + The notify() method wakes up one of the threads waiting for the + condition variable, if any are waiting. The notifyAll() method + wakes up all threads waiting for the condition variable. + + Note: the notify() and notifyAll() methods don't release the + lock; this means that the thread or threads awakened will not + return from their wait() call immediately, but only when the + thread that called notify() or notifyAll() finally relinquishes + ownership of the lock. + + Tip: the typical programming style using condition variables uses + the lock to synchronize access to some shared state; threads that + are interested in a particular change of state call wait() + repeatedly until they see the desired state, while threads that + modify the state call notify() or notifyAll() when they change the + state in such a way that it could possibly be a desired state for + one of the waiters. For example, the following code is a generic + producer-consumer situation with unlimited buffer capacity: + + # Consume one item + cv.acquire() + while not an_item_is_available(): + cv.wait() + get_an_available_item() + cv.release() + + # Produce one item + cv.acquire() + make_an_item_available() + cv.notify() + cv.release() + + To choose between notify() and notifyAll(), consider whether one + state change can be interesting for only one or several waiting + threads. E.g. in a typical producer-consumer situation, adding + one item to the buffer only needs to wake up one consumer thread. + + """ + + def __init__(self, lock=None): + """Constructor. + + If the lock argument is given and not None, it must be a Lock + or RLock object, and it is used as the underlying lock. + Otherwise, a new RLock object is created and used as the + underlying lock. + + """ + + def acquire(self, *args): + """Acquire the underlying lock. + + This method calls the corresponding method on the underlying + lock; the return value is whatever that method returns. + + """ + + def release(self): + """Release the underlying lock. + + This method calls the corresponding method on the underlying + lock; there is no return value. + + """ + + def wait(self, timeout=None): + """Wait until notified or until a timeout occurs. + + This must only be called when the calling thread has acquired + the lock. + + This method releases the underlying lock, and then blocks + until it is awakened by a notify() or notifyAll() call for the + same condition variable in another thread, or until the + optional timeout occurs. Once awakened or timed out, it + re-acquires the lock and returns. + + When the timeout argument is present and not None, it should + be a floating point number specifying a timeout for the + operation in seconds (or fractions thereof). + + When the underlying lock is an RLock, it is not released using + its release() method, since this may not actually unlock the + lock when it was acquired() multiple times recursively. + Instead, an internal interface of the RLock class is used, + which really unlocks it even when it has been recursively + acquired several times. Another internal interface is then + used to restore the recursion level when the lock is + reacquired. + + """ + + def notify(self): + """Wake up a thread waiting on this condition, if any. + + This must only be called when the calling thread has acquired + the lock. + + This method wakes up one of the threads waiting for the + condition variable, if any are waiting; it is a no-op if no + threads are waiting. + + The current implementation wakes up exactly one thread, if any + are waiting. However, it's not safe to rely on this behavior. + A future, optimized implementation may occasionally wake up + more than one thread. + + Note: the awakened thread does not actually return from its + wait() call until it can reacquire the lock. Since notify() + does not release the lock, its caller should. + + """ + + def notifyAll(self): + """Wake up all threads waiting on this condition. + + This method acts like notify(), but wakes up all waiting + threads instead of one. + + """ + + +class Semaphore: + """Semaphore object. + + This is one of the oldest synchronization primitives in the + history of computer science, invented by the early Dutch computer + scientist Edsger W. Dijkstra (he used P() and V() instead of + acquire() and release()). + + A semaphore manages an internal counter which is decremented by + each acquire() call and incremented by each release() call. The + counter can never go below zero; when acquire() finds that it is + zero, it blocks, waiting until some other thread calls release(). + + """ + + def __init__(self, value=1): + """Constructor. + + The optional argument gives the initial value for the internal + counter; it defaults to 1. + + """ + + def acquire(self, blocking=1): + """Acquire a semaphore. + + When invoked without arguments: if the internal counter is + larger than zero on entry, decrement it by one and return + immediately. If it is zero on entry, block, waiting until + some other thread has called release() to make it larger than + zero. This is done with proper interlocking so that if + multiple acquire() calls are blocked, release() will wake + exactly one of them up. The implementation may pick one at + random, so the order in which blocked threads are awakened + should not be relied on. There is no return value in this + case. + + When invoked with the 'blocking' argument set to true, do the + same thing as when called without arguments, and return true. + + When invoked with the 'blocking' argument set to false, do not + block. If a call without argument would block, return false + immediately; otherwise, do the same thing as when called + without arguments, and return true. + + """ + + def release(self): + """Release a semaphore. + + Increment the internal counter by one. When it was zero on + entry and another thread is waiting for it to become larger + than zero again, wake up that thread. + + """ + + +class Event: + """Event object. + + This is one of the simplest mechanisms for communication between + threads: one thread signals an event and another thread, or + threads, wait for it. + + An event object manages an internal flag that can be set to true + with the set() method and reset to false with the clear() method. + The wait() method blocks until the flag is true. + + """ + + def __init__(self): + """Constructor. + + The internal flag is initially false. + + """ + + def isSet(self): + """Return true iff the internal flag is true.""" + + def set(self): + """Set the internal flag to true. + + All threads waiting for it to become true are awakened. + + Threads that call wait() once the flag is true will not block + at all. + + """ + + def clear(self): + """Reset the internal flag to false. + + Subsequently, threads calling wait() will block until set() is + called to set the internal flag to true again. + + """ + + def wait(self, timeout=None): + """Block until the internal flag is true. + + If the internal flag is true on entry, return immediately. + Otherwise, block until another thread calls set() to set the + flag to true, or until the optional timeout occurs. + + When the timeout argument is present and not None, it should + be a floating point number specifying a timeout for the + operation in seconds (or fractions thereof). + + """ + + +class Thread: + """Thread class. + + *** ONLY OVERRIDE THE __init__() AND run() METHODS OF THIS CLASS *** + + This class represents an activity that is run in a separate thread + of control. There are two ways to specify the activity: by + passing a callable object to the constructor, or by overriding the + run() method in a subclass. No other methods (except for the + constructor) should be overridden in a subclass. + + Once a thread object is created, its activity must be started by + calling the thread's start() method. This invokes the run() + method in a separate thread of control. + + Once the thread's activity is started, the thread is considered + 'alive' and 'active' (these concepts are almost, but not quite + exactly, the same; their definition is intentionally somewhat + vague). It stops being alive and active when its run() method + terminates -- either normally, or by raising an unhandled + exception. The isAlive() method tests whether the thread is + alive. + + Other threads can call a thread's join() method. This blocks the + calling thread until the thread whose join() method is called + is terminated. + + A thread has a name. The name can be passed to the constructor, + set with the setName() method, and retrieved with the getName() + method. + + A thread can be flagged as a ``daemon thread''. The significance + of this flag is that the entire Python program exits when only + daemon threads are left. The initial value is inherited from the + creating thread. The flag can be set with the setDaemon() method + and retrieved with the getDaemon() method. + + There is a ``main thread'' object; this corresponds to the + initial thread of control in the Python program. It is not a + daemon thread. + + There is the possibility that ``dummy thread objects'' are + created. These are thread objects corresponding to ``alien + threads''. These are threads of control started outside the + threading module, e.g. directly from C code. Dummy thread objects + have limited functionality; they are always considered alive, + active, and daemonic, and cannot be join()ed. They are never + deleted, since it is impossible to detect the termination of alien + threads. + + """ + + def __init__(self, group=None, target=None, name=None, + args=(), kwargs={}): + """Thread constructor. + + This constructor should always be called with keyword + arguments. Arguments are: + + group + Should be None; reserved for future extension when a + ThreadGroup class is implemented. + + target + Callable object to be invoked by the run() method. + Defaults to None, meaning nothing is called. + + name + The thread name. By default, a unique name is constructed + of the form ``Thread-N'' where N is a small decimal + number. + + args + Argument tuple for the target invocation. Defaults to (). + + kwargs + Keyword argument dictionary for the target invocation. + Defaults to {}. + + If the subclass overrides the constructor, it must make sure + to invoke the base class constructor (Thread.__init__()) + before doing anything else to the thread. + + """ + + def start(self): + """Start the thread's activity. + + This must be called at most once per thread object. It + arranges for the object's run() method to be invoked in a + separate thread of control. + + """ + + def run(self): + """Method representing the thread's activity. + + You may override this method in a subclass. The standard + run() method invokes the callable object passed as the + 'target' argument, if any, with sequential and keyword + arguments taken from the 'args' and 'kwargs' arguments, + respectively. + + """ + + def join(self, timeout=None): + """Wait until the thread terminates. + + This blocks the calling thread until the thread whose join() + method is called terminates -- either normally or through an + unhandled exception -- or until the optional timeout occurs. + + When the timeout argument is present and not None, it should + be a floating point number specifying a timeout for the + operation in seconds (or fractions thereof). + + A thread can be join()ed many times. + + A thread cannot join itself because this would cause a + deadlock. + + It is an error to attempt to join() a thread before it has + been started. + + """ + + def getName(self): + """Return the thread's name.""" + + def setName(self, name): + """Set the thread's name. + + The name is a string used for identification purposes only. + It has no semantics. Multiple threads may be given the same + name. The initial name is set by the constructor. + + """ + + def isAlive(self): + """Return whether the thread is alive. + + Roughly, a thread is alive from the moment the start() method + returns until its run() method terminates. + + """ + + def isDaemon(self): + """Return the thread's daemon flag.""" + + def setDaemon(self): + """Set the thread's daemon flag. + + This must be called before start() is called. + + The initial value is inherited from the creating thread. + + The entire Python program exits when no active non-daemon + threads are left. + + """ + + +# Module-level functions: + + +def currentThread(): + """Return the current Thread object. + + This function returns the Thread object corresponding to the + caller's thread of control. + + If the caller's thread of control was not created through the + threading module, a dummy thread object with limited functionality + is returned. + + """ + + +def activeCount(): + """Return the number of currently active Thread objects. + + The returned count is equal to the length of the list returned by + enumerate(). + + """ + + +def enumerate(): + """Return a list of all currently active Thread objects. + + The list includes daemonic threads, dummy thread objects created + by currentThread(), and the main thread. It excludes terminated + threads and threads that have not yet been started. + + """ |