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
3# found in the LICENSE file.
4
5"""A utility to run functions with timeouts and retries."""
6# pylint: disable=W0702
7
8import threading
9
10from pylib.utils import reraiser_thread
11from pylib.utils import watchdog_timer
12
13
14def Run(func, timeout, retries, args=None, kwargs=None):
15  """Runs the passed function in a separate thread with timeouts and retries.
16
17  Args:
18    func: the function to be wrapped.
19    timeout: the timeout in seconds for each try.
20    retries: the number of retries.
21    args: list of positional args to pass to |func|.
22    kwargs: dictionary of keyword args to pass to |func|.
23
24  Returns:
25    The return value of func(*args, **kwargs).
26  """
27  if not args:
28    args = []
29  if not kwargs:
30    kwargs = {}
31
32  # The return value uses a list because Python variables are references, not
33  # values. Closures make a copy of the reference, so updating the closure's
34  # reference wouldn't update where the original reference pointed.
35  ret = [None]
36  def RunOnTimeoutThread():
37    ret[0] = func(*args, **kwargs)
38
39  while True:
40    try:
41      name = 'TimeoutThread-for-%s' % threading.current_thread().name
42      thread_group = reraiser_thread.ReraiserThreadGroup(
43          [reraiser_thread.ReraiserThread(RunOnTimeoutThread, name=name)])
44      thread_group.StartAll()
45      thread_group.JoinAll(watchdog_timer.WatchdogTimer(timeout))
46      return ret[0]
47    except:
48      if retries <= 0:
49        raise
50      retries -= 1
51