diff -r 7a6a8a003553 Lib/threading.py --- a/Lib/threading.py Tue May 28 22:22:14 2013 -0400 +++ b/Lib/threading.py Wed May 29 11:23:18 2013 -0300 @@ -153,6 +153,10 @@ pass self._waiters = _deque() + @classmethod + def from_condition(cls, condition): + return cls(condition._lock) + def __enter__(self): return self._lock.__enter__() @@ -177,12 +181,14 @@ else: return True - def wait(self, timeout=None): - if not self._is_owned(): - raise RuntimeError("cannot wait on un-acquired lock") - waiter = _allocate_lock() - waiter.acquire() - self._waiters.append(waiter) + def _remove_waiter(self, waiter): + try: + self._waiters.remove(waiter) + except ValueError: + pass + + def _wait(self, waiter, timeout): + """ Wait on a possibly already acquired lock. """ saved_state = self._release_save() try: # restore state no matter what (e.g., KeyboardInterrupt) if timeout is None: @@ -193,20 +199,40 @@ gotit = waiter.acquire(True, timeout) else: gotit = waiter.acquire(False) - if not gotit: - try: - self._waiters.remove(waiter) - except ValueError: - pass +# if not gotit: +# self._remove_waiter(waiter) return gotit finally: self._acquire_restore(saved_state) - def wait_for(self, predicate, timeout=None): + @classmethod + def wait_for_any(cls, conditions, timeout=None): + """ Wait on a dictionary of conditions with predicates or an + iterable of conditions. + """ + if not conditions: + raise ValueError("at least one condition should be provided") + + some_cond = next(iter(conditions)) + if any(cond._lock is not some_cond._lock for cond in conditions): + raise ValueError("all the conditions must use the same lock") + + if not some_cond._is_owned(): + raise RuntimeError("cannot wait on un-acquired lock") + + try: + predicates = conditions.values() + except AttributeError: + predicates = None + endtime = None waittime = timeout - result = predicate() + result = predicates and any(f() for f in predicates) while not result: + waiter = _allocate_lock() + for cond in conditions: + cond._waiters.append(waiter) + if waittime is not None: if endtime is None: endtime = _time() + waittime @@ -214,10 +240,26 @@ waittime = endtime - _time() if waittime <= 0: break - self.wait(waittime) - result = predicate() + + waiter.acquire() + # Any condition of the dictionary can be used to wait. + result = some_cond._wait(waiter, waittime) + + for cond in conditions: + cond._remove_waiter(waiter) + + if not predicates: + break + result = any(f() for f in predicates) + return result + def wait(self, timeout=None): + return self.wait_for_any([self], timeout) + + def wait_for(self, predicate, timeout=None): + return self.wait_for_any({self: predicate}, timeout) + def notify(self, n=1): if not self._is_owned(): raise RuntimeError("cannot notify on un-acquired lock") @@ -226,11 +268,11 @@ if not waiters_to_notify: return for waiter in waiters_to_notify: - waiter.release() try: - all_waiters.remove(waiter) - except ValueError: - pass + waiter.release() + except RuntimeError: + pass # This waiter might have been released by another condition + self._remove_waiter(waiter) def notify_all(self): self.notify(len(self._waiters))