[Android] Lint pylib/utils.
[chromium-blink-merge.git] / build / android / pylib / utils / reraiser_thread.py
blobeed72c173ead636d8397a174ef3a92757f43e596
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.
5 """Thread and ThreadGroup that reraise exceptions on the main thread."""
6 # pylint: disable=W0212
8 import logging
9 import sys
10 import threading
11 import traceback
13 from pylib.utils import watchdog_timer
16 class TimeoutError(Exception):
17 """Module-specific timeout exception."""
18 pass
21 def LogThreadStack(thread):
22 """Log the stack for the given thread.
24 Args:
25 thread: a threading.Thread instance.
26 """
27 stack = sys._current_frames()[thread.ident]
28 logging.critical('*' * 80)
29 logging.critical('Stack dump for thread \'%s\'', thread.name)
30 logging.critical('*' * 80)
31 for filename, lineno, name, line in traceback.extract_stack(stack):
32 logging.critical('File: "%s", line %d, in %s', filename, lineno, name)
33 if line:
34 logging.critical(' %s', line.strip())
35 logging.critical('*' * 80)
38 class ReraiserThread(threading.Thread):
39 """Thread class that can reraise exceptions."""
41 def __init__(self, func, args=None, kwargs=None, name=None):
42 """Initialize thread.
44 Args:
45 func: callable to call on a new thread.
46 args: list of positional arguments for callable, defaults to empty.
47 kwargs: dictionary of keyword arguments for callable, defaults to empty.
48 name: thread name, defaults to Thread-N.
49 """
50 super(ReraiserThread, self).__init__(name=name)
51 if not args:
52 args = []
53 if not kwargs:
54 kwargs = {}
55 self.daemon = True
56 self._func = func
57 self._args = args
58 self._kwargs = kwargs
59 self._exc_info = None
61 def ReraiseIfException(self):
62 """Reraise exception if an exception was raised in the thread."""
63 if self._exc_info:
64 raise self._exc_info[0], self._exc_info[1], self._exc_info[2]
66 #override
67 def run(self):
68 """Overrides Thread.run() to add support for reraising exceptions."""
69 try:
70 self._func(*self._args, **self._kwargs)
71 except:
72 self._exc_info = sys.exc_info()
73 raise
76 class ReraiserThreadGroup(object):
77 """A group of ReraiserThread objects."""
79 def __init__(self, threads=None):
80 """Initialize thread group.
82 Args:
83 threads: a list of ReraiserThread objects; defaults to empty.
84 """
85 if not threads:
86 threads = []
87 self._threads = threads
89 def Add(self, thread):
90 """Add a thread to the group.
92 Args:
93 thread: a ReraiserThread object.
94 """
95 self._threads.append(thread)
97 def StartAll(self):
98 """Start all threads."""
99 for thread in self._threads:
100 thread.start()
102 def _JoinAll(self, watcher=watchdog_timer.WatchdogTimer(None)):
103 """Join all threads without stack dumps.
105 Reraises exceptions raised by the child threads and supports breaking
106 immediately on exceptions raised on the main thread.
108 Args:
109 watcher: Watchdog object providing timeout, by default waits forever.
111 alive_threads = self._threads[:]
112 while alive_threads:
113 for thread in alive_threads[:]:
114 if watcher.IsTimedOut():
115 raise TimeoutError('Timed out waiting for %d of %d threads.' %
116 (len(alive_threads), len(self._threads)))
117 # Allow the main thread to periodically check for interrupts.
118 thread.join(0.1)
119 if not thread.isAlive():
120 alive_threads.remove(thread)
121 # All threads are allowed to complete before reraising exceptions.
122 for thread in self._threads:
123 thread.ReraiseIfException()
125 def JoinAll(self, watcher=watchdog_timer.WatchdogTimer(None)):
126 """Join all threads.
128 Reraises exceptions raised by the child threads and supports breaking
129 immediately on exceptions raised on the main thread. Unfinished threads'
130 stacks will be logged on watchdog timeout.
132 Args:
133 watcher: Watchdog object providing timeout, by default waits forever.
135 try:
136 self._JoinAll(watcher)
137 except TimeoutError:
138 for thread in (t for t in self._threads if t.isAlive()):
139 LogThreadStack(thread)
140 raise