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 """Thread and ThreadGroup that reraise exceptions on the main thread.""" | 5 """Thread and ThreadGroup that reraise exceptions on the main thread.""" |
6 # pylint: disable=W0212 | 6 # pylint: disable=W0212 |
7 | 7 |
8 import logging | 8 import logging |
9 import sys | 9 import sys |
10 import threading | 10 import threading |
| 11 import time |
11 import traceback | 12 import traceback |
12 | 13 |
13 from devil.utils import watchdog_timer | 14 from devil.utils import watchdog_timer |
14 | 15 |
15 | 16 |
16 class TimeoutError(Exception): | 17 class TimeoutError(Exception): |
17 """Module-specific timeout exception.""" | 18 """Module-specific timeout exception.""" |
18 pass | 19 pass |
19 | 20 |
20 | 21 |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
97 Args: | 98 Args: |
98 thread: a ReraiserThread object. | 99 thread: a ReraiserThread object. |
99 """ | 100 """ |
100 self._threads.append(thread) | 101 self._threads.append(thread) |
101 | 102 |
102 def StartAll(self): | 103 def StartAll(self): |
103 """Start all threads.""" | 104 """Start all threads.""" |
104 for thread in self._threads: | 105 for thread in self._threads: |
105 thread.start() | 106 thread.start() |
106 | 107 |
107 def _JoinAll(self, watcher=None): | 108 def _JoinAll(self, watcher=None, timeout=None): |
108 """Join all threads without stack dumps. | 109 """Join all threads without stack dumps. |
109 | 110 |
110 Reraises exceptions raised by the child threads and supports breaking | 111 Reraises exceptions raised by the child threads and supports breaking |
111 immediately on exceptions raised on the main thread. | 112 immediately on exceptions raised on the main thread. |
112 | 113 |
113 Args: | 114 Args: |
114 watcher: Watchdog object providing timeout, by default waits forever. | 115 watcher: Watchdog object providing the thread timeout. If none is |
| 116 provided, the thread will never be timed out. |
| 117 timeout: An optional number of seconds to wait before timing out the join |
| 118 operation. This will not time out the threads. |
115 """ | 119 """ |
116 if watcher is None: | 120 if watcher is None: |
117 watcher = watchdog_timer.WatchdogTimer(None) | 121 watcher = watchdog_timer.WatchdogTimer(None) |
118 alive_threads = self._threads[:] | 122 alive_threads = self._threads[:] |
119 while alive_threads: | 123 end_time = (time.time() + timeout) if timeout else None |
| 124 while alive_threads and (end_time is None or end_time > time.time()): |
120 for thread in alive_threads[:]: | 125 for thread in alive_threads[:]: |
121 if watcher.IsTimedOut(): | 126 if watcher.IsTimedOut(): |
122 raise TimeoutError('Timed out waiting for %d of %d threads.' % | 127 raise TimeoutError('Timed out waiting for %d of %d threads.' % |
123 (len(alive_threads), len(self._threads))) | 128 (len(alive_threads), len(self._threads))) |
124 # Allow the main thread to periodically check for interrupts. | 129 # Allow the main thread to periodically check for interrupts. |
125 thread.join(0.1) | 130 thread.join(0.1) |
126 if not thread.isAlive(): | 131 if not thread.isAlive(): |
127 alive_threads.remove(thread) | 132 alive_threads.remove(thread) |
128 # All threads are allowed to complete before reraising exceptions. | 133 # All threads are allowed to complete before reraising exceptions. |
129 for thread in self._threads: | 134 for thread in self._threads: |
130 thread.ReraiseIfException() | 135 thread.ReraiseIfException() |
131 | 136 |
132 def JoinAll(self, watcher=None): | 137 def IsAlive(self): |
| 138 """Check whether any of the threads are still alive. |
| 139 |
| 140 Returns: |
| 141 Whether any of the threads are still alive. |
| 142 """ |
| 143 return any(t.isAlive() for t in self._threads) |
| 144 |
| 145 def JoinAll(self, watcher=None, timeout=None): |
133 """Join all threads. | 146 """Join all threads. |
134 | 147 |
135 Reraises exceptions raised by the child threads and supports breaking | 148 Reraises exceptions raised by the child threads and supports breaking |
136 immediately on exceptions raised on the main thread. Unfinished threads' | 149 immediately on exceptions raised on the main thread. Unfinished threads' |
137 stacks will be logged on watchdog timeout. | 150 stacks will be logged on watchdog timeout. |
138 | 151 |
139 Args: | 152 Args: |
140 watcher: Watchdog object providing timeout, by default waits forever. | 153 watcher: Watchdog object providing the thread timeout. If none is |
| 154 provided, the thread will never be timed out. |
| 155 timeout: An optional number of seconds to wait before timing out the join |
| 156 operation. This will not time out the threads. |
141 """ | 157 """ |
142 try: | 158 try: |
143 self._JoinAll(watcher) | 159 self._JoinAll(watcher, timeout) |
144 except TimeoutError: | 160 except TimeoutError: |
145 logging.critical('Timed out. Dumping threads.') | 161 logging.critical('Timed out. Dumping threads.') |
146 for thread in (t for t in self._threads if t.isAlive()): | 162 for thread in (t for t in self._threads if t.isAlive()): |
147 LogThreadStack(thread) | 163 LogThreadStack(thread) |
148 raise | 164 raise |
149 | 165 |
150 def GetAllReturnValues(self, watcher=None): | 166 def GetAllReturnValues(self, watcher=None): |
151 """Get all return values, joining all threads if necessary. | 167 """Get all return values, joining all threads if necessary. |
152 | 168 |
153 Args: | 169 Args: |
(...skipping 10 matching lines...) Expand all Loading... |
164 Args: | 180 Args: |
165 funcs: List of functions to perform on their own threads. | 181 funcs: List of functions to perform on their own threads. |
166 watcher: Watchdog object providing timeout, by default waits forever. | 182 watcher: Watchdog object providing timeout, by default waits forever. |
167 | 183 |
168 Returns: | 184 Returns: |
169 A list of return values in the order of the given functions. | 185 A list of return values in the order of the given functions. |
170 """ | 186 """ |
171 thread_group = ReraiserThreadGroup(ReraiserThread(f) for f in funcs) | 187 thread_group = ReraiserThreadGroup(ReraiserThread(f) for f in funcs) |
172 thread_group.StartAll() | 188 thread_group.StartAll() |
173 return thread_group.GetAllReturnValues(watcher=watcher) | 189 return thread_group.GetAllReturnValues(watcher=watcher) |
OLD | NEW |