diff -r c5e2ea9e3aa7 Lib/test/test_threading.py --- a/Lib/test/test_threading.py Mon Apr 08 00:26:43 2013 +0200 +++ b/Lib/test/test_threading.py Sun Apr 14 12:09:27 2013 -0400 @@ -813,6 +813,32 @@ self.callback_args.append((args[:], kwargs.copy())) self.callback_event.set() +class TimerPoolTests(BaseTestCase): + + def setUp(self): + BaseTestCase.setUp(self) + self.callback_sum = 0 + self.callback_event = threading.Event() + + def test_init(self): + timer_pool = threading.TimerPool() + timer1 = timer_pool.startTimer(0.01, self._callback_spy, [1]) + self.callback_event.wait() + self.callback_event.clear() + timer2 = timer_pool.startTimer(100.0, self._callback_spy, [2]) + timer3 = timer_pool.startTimer(0.01, self._callback_spy, [4]) + self.callback_event.wait() + self.callback_event.clear() + self.assertEqual(timer_pool.stopTimer(timer2), True) + timer4 = timer_pool.startTimer(0.01, self._callback_spy, [8]) + self.callback_event.wait() + self.callback_event.clear() + self.assertEqual(self.callback_sum, 13) + + def _callback_spy(self, value): + self.callback_sum += value + self.callback_event.set() + class LockTests(lock_tests.LockTests): locktype = staticmethod(threading.Lock) diff -r c5e2ea9e3aa7 Lib/threading.py --- a/Lib/threading.py Mon Apr 08 00:26:43 2013 +0200 +++ b/Lib/threading.py Sun Apr 14 12:09:27 2013 -0400 @@ -2,6 +2,8 @@ import sys as _sys import _thread +import heapq +from time import time from time import sleep as _sleep try: @@ -830,6 +832,55 @@ self.function(*self.args, **self.kwargs) self.finished.set() + +class TimerPool(Thread): + + def __init__(self): + Thread.__init__(self, name="TimerPool") + self.heapq = [] + heapq.heapify(self.heapq) + self.timerId = 0 + self.finished = Event() + + def startTimer(self, timeout, function, args=None, kwargs=None): + self.timerId += 1 + timer = [timeout + time(), {"id": self.timerId, "function": function, + "args": args if args is not None else [], + "kwargs": kwargs if kwargs is not None else {}}] + heapq.heappush(self.heapq, timer) + self._update() + return self.timerId + + def stopTimer(self, timerId): + for timer in self.heapq: + if timerId == timer[1]["id"]: + self.heapq.remove(timer) + heapq.heapify(self.heapq) + self._update() + return True + return False + + def _update(self): + if not self._started.is_set(): + self.start() + else: + self.finished.set() + + def run(self): + if len(self.heapq) == 0: + raise RuntimeError("cannot run when no timer is active") + while len(self.heapq) != 0: + timer = heapq.nsmallest(1, self.heapq)[0] + self.finished.wait(timer[0] - time()) + timer = heapq.nsmallest(1, self.heapq)[0] #could have changed + if time() >= timer[0]: + self.heapq.remove(timer) + timer[1]["function"](*timer[1]["args"], **timer[1]["kwargs"]) + self._started = Event() + self._stopped = False + self._stop() + + # Special thread class to represent the main thread # This is garbage collected through an exit handler