Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # Copyright 2013 The Chromium Authors. All rights reserved. | 1 # Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 """A utility to run functions with timeouts and retries.""" | 5 """A utility to run functions with timeouts and retries.""" |
| 6 # pylint: disable=W0702 | 6 # pylint: disable=W0702 |
| 7 | 7 |
| 8 import threading | 8 import threading |
| 9 import time | |
| 9 | 10 |
| 10 from pylib.utils import reraiser_thread | 11 from pylib.utils import reraiser_thread |
| 11 from pylib.utils import watchdog_timer | 12 from pylib.utils import watchdog_timer |
| 12 | 13 |
| 13 | 14 |
| 14 class TimeoutRetryThread(reraiser_thread.ReraiserThread): | 15 class TimeoutRetryThread(reraiser_thread.ReraiserThread): |
| 15 pass | 16 def __init__(self, *args, **kwargs): |
| 17 super(TimeoutRetryThread, self).__init__(*args, **kwargs) | |
| 18 self._timeout_expired = False | |
| 19 self._start = time.time() | |
| 20 | |
| 21 def ElapsedTime(self): | |
| 22 return time.time() - self._start | |
| 23 | |
| 24 def SetTimeoutAsExpired(self): | |
| 25 self._timeout_expired = True | |
|
perezju
2014/10/28 14:31:18
Have tried several names now, maybe RequestThreadT
jbudorick
2014/10/28 17:28:46
Yeah, I like that better. That implies that we're
| |
| 26 | |
| 27 def CheckTimeout(self): | |
| 28 if self._timeout_expired: | |
| 29 raise reraiser_thread.TimeoutError('Thread timed out') | |
| 16 | 30 |
| 17 | 31 |
| 18 def Run(func, timeout, retries, args=None, kwargs=None): | 32 def Run(func, timeout, retries, args=None, kwargs=None): |
| 19 """Runs the passed function in a separate thread with timeouts and retries. | 33 """Runs the passed function in a separate thread with timeouts and retries. |
| 20 | 34 |
| 21 Args: | 35 Args: |
| 22 func: the function to be wrapped. | 36 func: the function to be wrapped. |
| 23 timeout: the timeout in seconds for each try. | 37 timeout: the timeout in seconds for each try. |
| 24 retries: the number of retries. | 38 retries: the number of retries. |
| 25 args: list of positional args to pass to |func|. | 39 args: list of positional args to pass to |func|. |
| 26 kwargs: dictionary of keyword args to pass to |func|. | 40 kwargs: dictionary of keyword args to pass to |func|. |
| 27 | 41 |
| 28 Returns: | 42 Returns: |
| 29 The return value of func(*args, **kwargs). | 43 The return value of func(*args, **kwargs). |
| 30 """ | 44 """ |
| 31 if not args: | 45 if not args: |
| 32 args = [] | 46 args = [] |
| 33 if not kwargs: | 47 if not kwargs: |
| 34 kwargs = {} | 48 kwargs = {} |
| 35 | 49 |
| 36 # The return value uses a list because Python variables are references, not | 50 # The return value uses a list because Python variables are references, not |
| 37 # values. Closures make a copy of the reference, so updating the closure's | 51 # values. Closures make a copy of the reference, so updating the closure's |
| 38 # reference wouldn't update where the original reference pointed. | 52 # reference wouldn't update where the original reference pointed. |
| 39 ret = [None] | 53 ret = [None] |
| 40 def RunOnTimeoutThread(): | 54 def RunOnTimeoutThread(): |
| 41 ret[0] = func(*args, **kwargs) | 55 ret[0] = func(*args, **kwargs) |
| 42 | 56 |
| 57 num_try = 1 | |
| 43 while True: | 58 while True: |
| 59 child_thread = TimeoutRetryThread( | |
| 60 RunOnTimeoutThread, | |
| 61 name='TimeoutThread-%d-for-%s' % (num_try, | |
| 62 threading.current_thread().name)) | |
| 44 try: | 63 try: |
| 45 name = 'TimeoutThread-for-%s' % threading.current_thread().name | 64 thread_group = reraiser_thread.ReraiserThreadGroup([child_thread]) |
| 46 thread_group = reraiser_thread.ReraiserThreadGroup( | |
| 47 [TimeoutRetryThread(RunOnTimeoutThread, name=name)]) | |
| 48 thread_group.StartAll() | 65 thread_group.StartAll() |
| 49 thread_group.JoinAll(watchdog_timer.WatchdogTimer(timeout)) | 66 thread_group.JoinAll(watchdog_timer.WatchdogTimer(timeout)) |
| 50 return ret[0] | 67 return ret[0] |
| 51 except: | 68 except: |
| 52 if retries <= 0: | 69 child_thread.SetTimeoutAsExpired() |
|
jbudorick
2014/10/28 17:28:46
I think we only want to do this if we catch a time
| |
| 70 if num_try > retries: | |
| 53 raise | 71 raise |
| 54 retries -= 1 | 72 num_try += 1 |
| OLD | NEW |