diff --git a/Lib/threading.py b/Lib/threading.py --- a/Lib/threading.py +++ b/Lib/threading.py @@ -1,4 +1,7 @@ -"""Thread module emulating a subset of Java's threading model.""" +""" +Thread module emulating a subset of Java's threading model for thread-based +parallelism. +""" import sys as _sys import _thread @@ -9,11 +12,11 @@ from _weakrefset import WeakSet # Note regarding PEP 8 compliant names -# This threading model was originally inspired by Java, and inherited +# This threading model was originally inspired by Java, and inherited # the convention of camelCase function and method names from that -# language. Those originaly names are not in any imminent danger of -# being deprecated (even for Py3k),so this module provides them as an -# alias for the PEP 8 compliant names +# language. Those original names are not in any imminent danger of +# being deprecated (even for Py3k), so this module provides them as an +# alias for the PEP 8 compliant names. # Note that using the new PEP 8 compliant names facilitates substitution # with the multiprocessing module, which doesn't provide the old # Java inspired names. @@ -87,6 +90,13 @@ Lock = _allocate_lock def RLock(verbose=None, *args, **kwargs): + """ + 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. + """ if verbose is None: verbose = _VERBOSE if (__debug__ and verbose) or _CRLock is None: @@ -111,6 +121,10 @@ self.__class__.__name__, owner, self._count) def acquire(self, blocking=True, timeout=-1): + """ + Acquire a reentrant lock, blocking or non-blocking, with an optional + timeout (in seconds). + """ me = get_ident() if self._owner == me: self._count = self._count + 1 @@ -131,6 +145,14 @@ __enter__ = acquire def release(self): + """ + Release a lock, decrementing 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. + """ if self._owner != get_ident(): raise RuntimeError("cannot release un-acquired lock") self._count = count = self._count - 1 @@ -173,8 +195,17 @@ class Condition(_Verbose): + """ + A condition variable allows one or more threads to wait until they are + notified by another thread. + """ def __init__(self, lock=None, verbose=None): + """ + 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. + """ _Verbose.__init__(self, verbose) if lock is None: lock = RLock() @@ -224,6 +255,11 @@ return True def wait(self, timeout=None): + """ + Wait until notified or until an optional timeout (in seconds) occurs. + If the calling thread has not acquired the lock when this method is + called, a RuntimeError is raised. + """ if not self._is_owned(): raise RuntimeError("cannot wait on un-acquired lock") waiter = _allocate_lock() @@ -256,6 +292,16 @@ self._acquire_restore(saved_state) def wait_for(self, predicate, timeout=None): + """ + Wait until a condition evaluates to True. 'predicate' should be a + callable which result will be interpreted as a boolean value. An + optional timeout, in seconds, may be provided giving the maximum time + to wait. + This utility method may call wait() repeatedly until the predicate is + satisfied, or until a timeout occurs. The return value is the last + return value of the predicate and will evaluate to False if the method + timed out. + """ endtime = None waittime = timeout result = predicate() @@ -282,6 +328,13 @@ return result def notify(self, n=1): + """ + Wake up a thread waiting on this condition, if any, where the optional + 'n' specifies the n-thread in the list of threads waiting on this + condition; by default, n = 1. + If the calling thread has not acquired the lock when this method is + called, a RuntimeError is raised. + """ if not self._is_owned(): raise RuntimeError("cannot notify on un-acquired lock") __waiters = self._waiters @@ -300,16 +353,32 @@ pass def notify_all(self): + """ + Wake up all threads waiting on this condition. This method acts like + notify(), but wakes up all waiting threads instead of one. If the + calling thread has not acquired the lock when this method is called, a + RuntimeError is raised. + """ self.notify(len(self._waiters)) notifyAll = notify_all class Semaphore(_Verbose): - + """ + A semaphore is a synchronisation mechanism 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(). + """ # After Tim Peters' semaphore class, but not quite the same (no maximum) def __init__(self, value=1, verbose=None): + """ + The optional 'value' argument gives the initial value for the internal + counter; it defaults to 1. If the value given is less than 0, ValueError + is raised. + """ if value < 0: raise ValueError("semaphore initial value must be>= 0") _Verbose.__init__(self, verbose) @@ -317,6 +386,15 @@ self._value = value def acquire(self, blocking=True, timeout=None): + """ + Acquire a semaphore. + When invoked with blocking set to False, do not block. If a call without + an argument would block, return False immediately; otherwise, do the + same thing as when called without arguments, and return True. + When invoked with a timeout other than None, it will block for at most + timeout seconds. If acquire does not complete successfully in that + interval, return False. Return True otherwise. + """ if not blocking and timeout is not None: raise ValueError("can't specify timeout for non-blocking acquire") rc = False @@ -348,6 +426,11 @@ __enter__ = acquire def release(self): + """ + Release a semaphore, incrementing 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. + """ self._cond.acquire() self._value = self._value + 1 if __debug__: @@ -361,22 +444,42 @@ class BoundedSemaphore(Semaphore): - """Semaphore that checks that # releases is <= # acquires""" + """ + A bounded semaphore checks to make sure its current value doesn’t exceed + its initial value. If it does, ValueError is raised. + """ + def __init__(self, value=1, verbose=None): + """ + The optional 'value' argument gives the initial value for the internal + counter; it defaults to 1. If the value given is less than 0, ValueError + is raised. + """ Semaphore.__init__(self, value, verbose) self._initial_value = value def release(self): + """ + Release the semaphore; raises ValueError if the bounded semaphore has + been released too many times. + """ if self._value>= self._initial_value: raise ValueError("Semaphore released too many times") return Semaphore.release(self) class Event(_Verbose): - + """ + 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. + """ # After Tim Peters' event class (without is_posted()) def __init__(self, verbose=None): + """ + The internal flag is initially False. + """ _Verbose.__init__(self, verbose) self._cond = Condition(Lock()) self._flag = False @@ -386,11 +489,17 @@ self._cond.__init__() def is_set(self): + """Return True if and only if the internal flag is True.""" return self._flag isSet = is_set 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. + """ self._cond.acquire() try: self._flag = True @@ -399,6 +508,11 @@ self._cond.release() 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. + """ self._cond.acquire() try: self._flag = False @@ -406,6 +520,17 @@ self._cond.release() 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). + This method returns the internal flag on exit, so it will always return + True except if a timeout is given and the operation times out. + """ self._cond.acquire() try: if not self._flag: @@ -428,18 +553,23 @@ # and a 'broken' state in which all threads get get the exception. class Barrier(_Verbose): """ - Barrier. Useful for synchronizing a fixed number of threads - at known synchronization points. Threads block on 'wait()' and are - simultaneously once they have all made that call. + This class provides a simple synchronization primitive for use by a fixed + number of threads that need to wait for each other. Each of the threads + tries to pass the barrier by calling the wait() method and will block until + all of the threads have made the call. At this points, the threads are + released simultanously. + The barrier can be reused any number of times for the same number of + threads. """ + def __init__(self, parties, action=None, timeout=None, verbose=None): """ Create a barrier, initialised to 'parties' threads. - 'action' is a callable which, when supplied, will be called - by one of the threads after they have all entered the - barrier and just prior to releasing them all. - If a 'timeout' is provided, it is uses as the default for - all subsequent 'wait()' calls. + 'action' is a callable which, when supplied, will be called by one of + the threads after they have all entered the barrier and just prior to + releasing them all. + If a 'timeout' is provided, it is uses as the default for all subsequent + wait() calls. """ _Verbose.__init__(self, verbose) self._cond = Condition(Lock()) @@ -451,10 +581,10 @@ def wait(self, timeout=None): """ - Wait for the barrier. When the specified number of threads have - started waiting, they are all simultaneously awoken. If an 'action' - was provided for the barrier, one of the threads will have executed - that callback prior to returning. + Wait for the barrier, with an optional timeout (in seconds). When the + specified number of threads have started waiting, they are all + simultaneously awoken. If an 'action' was provided for the barrier, one + of the threads will have executed that callback prior to returning. Returns an individual index number from 0 to 'parties-1'. """ if timeout is None: @@ -523,9 +653,8 @@ def reset(self): """ - Reset the barrier to the initial state. - Any threads currently waiting will get the BrokenBarrier exception - raised. + Reset the barrier to the initial state. Any threads currently waiting + will get the BrokenBarrier exception raised. """ with self._cond: if self._count> 0: @@ -558,9 +687,7 @@ @property def parties(self): - """ - Return the number of threads required to trip the barrier. - """ + """Return the number of threads required to trip the barrier.""" return self._parties @property @@ -576,13 +703,13 @@ @property def broken(self): - """ - Return True if the barrier is in a broken state - """ + """Return True if the barrier is in a broken state.""" return self._state == -2 -#exception raised by the Barrier class -class BrokenBarrierError(RuntimeError): pass + +class BrokenBarrierError(RuntimeError): + """Exception raised by the Barrier class.""" + pass # Helper to generate new thread names @@ -603,6 +730,10 @@ # Main class for threads class Thread(_Verbose): + """ + A class that represents a thread of control. This class can be safely + subclassed in a limited fashion. + """ __initialized = False # Need to store a reference to sys.exc_info for printing @@ -616,6 +747,29 @@ def __init__(self, group=None, target=None, name=None, args=(), kwargs=None, verbose=None, *, daemon=None): + """ + 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' is the callable object to be invoked by the run() method. + Defaults to None, meaning nothing is called. + + 'name' is the thread name. By default, a unique name is constructed of + the form "Thread-N" where N is a small decimal number. + + 'args' is the argument tuple for the target invocation. Defaults to (). + + 'kwargs' is a dictionary of keyword arguments for the target invocation. + Defaults to {}. + + 'verbose' is a flag used for debugging messages. + + If not None, 'daemon' explicitly sets whether the thread is daemonic. + If None (the default), the daemonic property is inherited from the current thread. + """ assert group is None, "group argument must be None for now" _Verbose.__init__(self, verbose) if kwargs is None: @@ -659,6 +813,13 @@ return "<%s(%s, %s)>" % (self.__class__.__name__, self._name, status) def start(self): + """ + Start the thread’s activity. + It 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. + This method will raise a RuntimeError if called more than once on the + same thread object. + """ if not self._initialized: raise RuntimeError("thread.__init__() not called") @@ -677,6 +838,13 @@ self._started.wait() 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 to the object’s constructor as the + target argument, if any, with sequential and keyword arguments taken + from the args and kwargs arguments, respectively. + """ try: if self._target: self._target(*self._args, **self._kwargs) @@ -825,6 +993,11 @@ raise 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. + """ if not self._initialized: raise RuntimeError("Thread.__init__() not called") if not self._started.is_set(): @@ -860,6 +1033,11 @@ @property def name(self): + """ + 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. + """ assert self._initialized, "Thread.__init__() not called" return self._name @@ -870,10 +1048,22 @@ @property def ident(self): + """ + Returns the ‘thread identifier’ of this thread or None if the thread has + not been started. This is a nonzero integer. See the get_ident() + function. Thread identifiers may be recycled when a thread exits and + another thread is created. The identifier is available even after the + thread has exited. + """ assert self._initialized, "Thread.__init__() not called" return self._ident def is_alive(self): + """ + Return whether the thread is alive. + This method returns True just before the run() method starts until just + after the run() method terminates. + """ assert self._initialized, "Thread.__init__() not called" return self._started.is_set() and not self._stopped @@ -881,6 +1071,11 @@ @property def daemon(self): + """ + A boolean value indicating whether this thread is a daemon thread (True) + or not (False). This must be set before start() is called, otherwise + RuntimeError is raised. + """ assert self._initialized, "Thread.__init__() not called" return self._daemonic @@ -907,14 +1102,20 @@ # The timer class was contributed by Itamar Shtull-Trauring class Timer(Thread): - """Call a function after a specified number of seconds: - + """ + A thread that executes a function after a specified number of seconds has + passed: + 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={}): + """ + Create a timer that will run function with arguments 'args' and keyword + arguments 'kwargs', after 'interval' seconds have passed. + """ Thread.__init__(self) self.interval = interval self.function = function @@ -923,7 +1124,10 @@ self.finished = Event() def cancel(self): - """Stop the timer if it hasn't finished yet""" + """ + Stop the timer, and cancel the execution of the timer’s action. This + will only work if the timer is still in its waiting stage. + """ self.finished.set() def run(self): @@ -994,6 +1198,12 @@ # Global API functions def current_thread(): + """ + Return the current 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. + """ try: return _active[get_ident()] except KeyError: @@ -1002,6 +1212,10 @@ currentThread = current_thread def active_count(): + """ + Return the number of Thread objects currently alive. The returned count is + equal to the length of the list returned by enumerate(). + """ with _active_limbo_lock: return len(_active) + len(_limbo) @@ -1012,6 +1226,12 @@ return list(_active.values()) + list(_limbo.values()) def enumerate(): + """ + Return a list of all Thread objects currently alive. The list includes + daemonic threads, dummy thread objects created by current_thread(), and the + main thread. It excludes terminated threads and threads that have not yet + been started. + """ with _active_limbo_lock: return list(_active.values()) + list(_limbo.values()) diff --git a/Misc/ACKS b/Misc/ACKS --- a/Misc/ACKS +++ b/Misc/ACKS @@ -206,6 +206,7 @@ Ryan Coyner Christopher A. Craig Laura Creighton +Graeme Cross Simon Cross Drew Csillag Joaquin Cuenca Abela