| 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 | 6 |
| 7 import logging | 7 import logging |
| 8 import sys | 8 import sys |
| 9 import threading | 9 import threading |
| 10 import time | 10 import time |
| 11 import traceback | 11 import traceback |
| 12 | 12 |
| 13 import watchdog_timer | 13 import watchdog_timer |
| 14 | 14 |
| 15 | 15 |
| 16 class TimeoutError(Exception): | 16 class TimeoutError(Exception): |
| 17 """Module-specific timeout exception.""" | 17 """Module-specific timeout exception.""" |
| 18 pass | 18 pass |
| 19 | 19 |
| 20 | 20 |
| 21 def LogThreadStack(thread): |
| 22 """Log the stack for the given thread. |
| 23 |
| 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) |
| 36 |
| 37 |
| 21 class ReraiserThread(threading.Thread): | 38 class ReraiserThread(threading.Thread): |
| 22 """Thread class that can reraise exceptions.""" | 39 """Thread class that can reraise exceptions.""" |
| 23 | 40 |
| 24 def __init__(self, func, args=[], kwargs={}, name=None): | 41 def __init__(self, func, args=[], kwargs={}, name=None): |
| 25 """Initialize thread. | 42 """Initialize thread. |
| 26 | 43 |
| 27 Args: | 44 Args: |
| 28 func: callable to call on a new thread. | 45 func: callable to call on a new thread. |
| 29 args: list of positional arguments for callable, defaults to empty. | 46 args: list of positional arguments for callable, defaults to empty. |
| 30 kwargs: dictionary of keyword arguments for callable, defaults to empty. | 47 kwargs: dictionary of keyword arguments for callable, defaults to empty. |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 106 immediately on exceptions raised on the main thread. Unfinished threads' | 123 immediately on exceptions raised on the main thread. Unfinished threads' |
| 107 stacks will be logged on watchdog timeout. | 124 stacks will be logged on watchdog timeout. |
| 108 | 125 |
| 109 Args: | 126 Args: |
| 110 watcher: Watchdog object providing timeout, by default waits forever. | 127 watcher: Watchdog object providing timeout, by default waits forever. |
| 111 """ | 128 """ |
| 112 try: | 129 try: |
| 113 self._JoinAll(watcher) | 130 self._JoinAll(watcher) |
| 114 except TimeoutError: | 131 except TimeoutError: |
| 115 for thread in (t for t in self._threads if t.isAlive()): | 132 for thread in (t for t in self._threads if t.isAlive()): |
| 116 stack = sys._current_frames()[thread.ident] | 133 LogThreadStack(thread) |
| 117 logging.critical('*' * 80) | |
| 118 logging.critical('Stack dump for timed out thread \'%s\'', thread.name) | |
| 119 logging.critical('*' * 80) | |
| 120 for filename, lineno, name, line in traceback.extract_stack(stack): | |
| 121 logging.critical('File: "%s", line %d, in %s', filename, lineno, name) | |
| 122 if line: | |
| 123 logging.critical(' %s', line.strip()) | |
| 124 logging.critical('*' * 80) | |
| 125 raise | 134 raise |
| OLD | NEW |