def Run(func, timeout, retries, args=[], kwargs={}): """Runs the passed function in a separate thread with timeouts and retries. Args: func: the function to be wrapped. timeout: the timeout in seconds for each try. retries: the number of retries. args: list of positional args to pass to |func|. kwargs: dictionary of keyword args to pass to |func|. Returns: The return value of func(*args, **kwargs). """ # The return value uses a list because Python variables are references, not # values. Closures make a copy of the reference, so updating the closure's # reference wouldn't update where the original reference pointed. ret = [None] def RunOnTimeoutThread(): ret[0] = func(*args, **kwargs) while True: try: name = 'TimeoutThread-for-%s' % threading.current_thread().name thread_group = reraiser_thread.ReraiserThreadGroup( [reraiser_thread.ReraiserThread(RunOnTimeoutThread, name=name)]) thread_group.StartAll() thread_group.JoinAll(watchdog_timer.WatchdogTimer(timeout)) return ret[0] except: if retries <= 0: raise retries -= 1
def testJoinRaise(self): def f(): raise TestException group = reraiser_thread.ReraiserThreadGroup( [reraiser_thread.ReraiserThread(f) for _ in xrange(5)]) group.StartAll() with self.assertRaises(TestException): group.JoinAll()
def testInit(self): ran = [False] * 5 def f(i): ran[i] = True group = reraiser_thread.ReraiserThreadGroup( [reraiser_thread.ReraiserThread(f, args=[i]) for i in range(5)]) group.StartAll() group.JoinAll() for v in ran: self.assertTrue(v)
def testJoinTimeout(self): def f(): pass event = threading.Event() def g(): event.wait() group = reraiser_thread.ReraiserThreadGroup([ reraiser_thread.ReraiserThread(g), reraiser_thread.ReraiserThread(f) ]) group.StartAll() with self.assertRaises(reraiser_thread.TimeoutError): group.JoinAll(watchdog_timer.WatchdogTimer(0.01)) event.set()