Craig Harrison | e3ea8f2 | 2012-10-01 13:55:21 -0700 | [diff] [blame] | 1 | # Copyright (c) 2012 The Chromium OS 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 | |
Craig Harrison | 922fcb9 | 2012-10-31 09:06:22 -0700 | [diff] [blame^] | 5 | import logging |
| 6 | import sys |
Craig Harrison | e3ea8f2 | 2012-10-01 13:55:21 -0700 | [diff] [blame] | 7 | import threading |
Craig Harrison | fc459df | 2012-10-04 13:49:48 -0700 | [diff] [blame] | 8 | import time |
Craig Harrison | e3ea8f2 | 2012-10-01 13:55:21 -0700 | [diff] [blame] | 9 | |
| 10 | |
Craig Harrison | fc459df | 2012-10-04 13:49:48 -0700 | [diff] [blame] | 11 | class BaseStressor(threading.Thread): |
| 12 | """ |
| 13 | Implements common functionality for *Stressor classes. |
| 14 | |
| 15 | @var stressor: callable which performs a single stress event. |
| 16 | """ |
Craig Harrison | 922fcb9 | 2012-10-31 09:06:22 -0700 | [diff] [blame^] | 17 | def __init__(self, stressor, escalate_exceptions=True): |
Craig Harrison | e3ea8f2 | 2012-10-01 13:55:21 -0700 | [diff] [blame] | 18 | """ |
Craig Harrison | fc459df | 2012-10-04 13:49:48 -0700 | [diff] [blame] | 19 | Initialize the ControlledStressor. |
| 20 | |
| 21 | @param stressor: callable which performs a single stress event. |
Craig Harrison | 922fcb9 | 2012-10-31 09:06:22 -0700 | [diff] [blame^] | 22 | @param escalate_exceptions: whether to escalate exceptions to the parent |
| 23 | thread; defaults to True. |
Craig Harrison | fc459df | 2012-10-04 13:49:48 -0700 | [diff] [blame] | 24 | """ |
| 25 | super(BaseStressor, self).__init__() |
Craig Harrison | e3ea8f2 | 2012-10-01 13:55:21 -0700 | [diff] [blame] | 26 | self.daemon = True |
Craig Harrison | fc459df | 2012-10-04 13:49:48 -0700 | [diff] [blame] | 27 | self.stressor = stressor |
Craig Harrison | 922fcb9 | 2012-10-31 09:06:22 -0700 | [diff] [blame^] | 28 | self._escalate_exceptions = escalate_exceptions |
| 29 | self._exc_info = None |
Craig Harrison | fc459df | 2012-10-04 13:49:48 -0700 | [diff] [blame] | 30 | |
| 31 | |
| 32 | def start(self, start_condition=None): |
| 33 | """ |
| 34 | Creates a new thread which will call the run() method. |
| 35 | |
| 36 | Optionally takes a wait condition before the stressor loop. Returns |
| 37 | immediately. |
| 38 | |
Craig Harrison | 922fcb9 | 2012-10-31 09:06:22 -0700 | [diff] [blame^] | 39 | @param start_condition: the new thread will wait until this optional |
Craig Harrison | fc459df | 2012-10-04 13:49:48 -0700 | [diff] [blame] | 40 | callable returns True before running the stressor. |
| 41 | """ |
| 42 | self._start_condition = start_condition |
| 43 | super(BaseStressor, self).start() |
Craig Harrison | e3ea8f2 | 2012-10-01 13:55:21 -0700 | [diff] [blame] | 44 | |
| 45 | |
| 46 | def run(self): |
Craig Harrison | fc459df | 2012-10-04 13:49:48 -0700 | [diff] [blame] | 47 | """ |
| 48 | Introduce a delay then start the stressor loop. |
| 49 | |
| 50 | Overloaded from threading.Thread. This is run in a separate thread when |
| 51 | start() is called. |
| 52 | """ |
| 53 | if self._start_condition: |
| 54 | while not self._start_condition(): |
| 55 | time.sleep(1) |
Craig Harrison | 922fcb9 | 2012-10-31 09:06:22 -0700 | [diff] [blame^] | 56 | try: |
| 57 | self._loop_stressor() |
| 58 | except Exception as e: |
| 59 | if self._escalate_exceptions: |
| 60 | self._exc_info = sys.exc_info() |
| 61 | raise |
Craig Harrison | fc459df | 2012-10-04 13:49:48 -0700 | [diff] [blame] | 62 | |
| 63 | |
| 64 | def _loop_stressor(self): |
| 65 | """ |
| 66 | Apply stressor in a loop. |
| 67 | |
| 68 | Overloaded by the particular *Stressor. |
| 69 | """ |
| 70 | raise NotImplementedError |
| 71 | |
| 72 | |
Craig Harrison | 922fcb9 | 2012-10-31 09:06:22 -0700 | [diff] [blame^] | 73 | def reraise(self): |
| 74 | """ |
| 75 | Reraise an exception raised in the thread's stress loop. |
| 76 | |
| 77 | This is a No-op if no exception was raised. |
| 78 | """ |
| 79 | if self._exc_info: |
| 80 | exc_info = self._exc_info |
| 81 | self._exc_info = None |
| 82 | raise exc_info[0], exc_info[1], exc_info[2] |
| 83 | |
| 84 | |
Craig Harrison | fc459df | 2012-10-04 13:49:48 -0700 | [diff] [blame] | 85 | class ControlledStressor(BaseStressor): |
| 86 | """ |
| 87 | Run a stressor in loop on a separate thread. |
| 88 | |
| 89 | Creates a new thread and calls |stressor| in a loop until stop() is called. |
| 90 | """ |
Craig Harrison | 922fcb9 | 2012-10-31 09:06:22 -0700 | [diff] [blame^] | 91 | def __init__(self, stressor, escalate_exceptions=True): |
Craig Harrison | fc459df | 2012-10-04 13:49:48 -0700 | [diff] [blame] | 92 | """ |
| 93 | Initialize the ControlledStressor. |
| 94 | |
| 95 | @param stressor: callable which performs a single stress event. |
Craig Harrison | 922fcb9 | 2012-10-31 09:06:22 -0700 | [diff] [blame^] | 96 | @param escalate_exceptions: whether to escalate exceptions to the parent |
| 97 | thread; defaults to True. |
Craig Harrison | fc459df | 2012-10-04 13:49:48 -0700 | [diff] [blame] | 98 | """ |
| 99 | self._complete = threading.Event() |
Craig Harrison | 922fcb9 | 2012-10-31 09:06:22 -0700 | [diff] [blame^] | 100 | super(ControlledStressor, self).__init__(stressor, escalate_exceptions) |
Craig Harrison | fc459df | 2012-10-04 13:49:48 -0700 | [diff] [blame] | 101 | |
| 102 | |
| 103 | def _loop_stressor(self): |
| 104 | """Overloaded from parent.""" |
Craig Harrison | 922fcb9 | 2012-10-31 09:06:22 -0700 | [diff] [blame^] | 105 | iteration_num = 0 |
Craig Harrison | e3ea8f2 | 2012-10-01 13:55:21 -0700 | [diff] [blame] | 106 | while not self._complete.is_set(): |
Craig Harrison | 922fcb9 | 2012-10-31 09:06:22 -0700 | [diff] [blame^] | 107 | iteration_num += 1 |
| 108 | logging.info('Stressor iteration: %d' % iteration_num) |
Craig Harrison | fc459df | 2012-10-04 13:49:48 -0700 | [diff] [blame] | 109 | self.stressor() |
Craig Harrison | e3ea8f2 | 2012-10-01 13:55:21 -0700 | [diff] [blame] | 110 | |
| 111 | |
Craig Harrison | fc459df | 2012-10-04 13:49:48 -0700 | [diff] [blame] | 112 | def start(self, start_condition=None): |
| 113 | """Start applying the stressor. |
| 114 | |
| 115 | Overloaded from parent. |
| 116 | |
| 117 | @param start_condition: the new thread will wait to until this optional |
| 118 | callable returns True before running the stressor. |
| 119 | """ |
Craig Harrison | e3ea8f2 | 2012-10-01 13:55:21 -0700 | [diff] [blame] | 120 | self._complete.clear() |
Craig Harrison | fc459df | 2012-10-04 13:49:48 -0700 | [diff] [blame] | 121 | super(ControlledStressor, self).start(start_condition) |
Craig Harrison | e3ea8f2 | 2012-10-01 13:55:21 -0700 | [diff] [blame] | 122 | |
| 123 | |
| 124 | def stop(self, timeout=45): |
Craig Harrison | fc459df | 2012-10-04 13:49:48 -0700 | [diff] [blame] | 125 | """ |
| 126 | Stop applying the stressor. |
Craig Harrison | e3ea8f2 | 2012-10-01 13:55:21 -0700 | [diff] [blame] | 127 | |
Craig Harrison | fc459df | 2012-10-04 13:49:48 -0700 | [diff] [blame] | 128 | @param timeout: maximum time to wait for a single run of the stressor to |
| 129 | complete, defaults to 45 seconds. |
| 130 | """ |
Craig Harrison | e3ea8f2 | 2012-10-01 13:55:21 -0700 | [diff] [blame] | 131 | self._complete.set() |
| 132 | self.join(timeout) |
Craig Harrison | 922fcb9 | 2012-10-31 09:06:22 -0700 | [diff] [blame^] | 133 | self.reraise() |
Craig Harrison | e3ea8f2 | 2012-10-01 13:55:21 -0700 | [diff] [blame] | 134 | |
| 135 | |
Craig Harrison | fc459df | 2012-10-04 13:49:48 -0700 | [diff] [blame] | 136 | class CountedStressor(BaseStressor): |
| 137 | """ |
| 138 | Run a stressor in a loop on a separate thread a given number of times. |
Craig Harrison | e3ea8f2 | 2012-10-01 13:55:21 -0700 | [diff] [blame] | 139 | |
Craig Harrison | 922fcb9 | 2012-10-31 09:06:22 -0700 | [diff] [blame^] | 140 | Creates a new thread and calls |stressor| in a loop |iterations| times. The |
Craig Harrison | fc459df | 2012-10-04 13:49:48 -0700 | [diff] [blame] | 141 | calling thread can use wait() to block until the loop completes. |
| 142 | """ |
| 143 | def _loop_stressor(self): |
| 144 | """Overloaded from parent.""" |
Craig Harrison | 922fcb9 | 2012-10-31 09:06:22 -0700 | [diff] [blame^] | 145 | for iteration_num in xrange(1, self._iterations + 1): |
| 146 | logging.info('Stressor iteration: %d of %d' % (iteration_num, |
| 147 | self._iterations)) |
Craig Harrison | fc459df | 2012-10-04 13:49:48 -0700 | [diff] [blame] | 148 | self.stressor() |
Craig Harrison | e3ea8f2 | 2012-10-01 13:55:21 -0700 | [diff] [blame] | 149 | |
| 150 | |
Craig Harrison | 922fcb9 | 2012-10-31 09:06:22 -0700 | [diff] [blame^] | 151 | def start(self, iterations, start_condition=None): |
Craig Harrison | fc459df | 2012-10-04 13:49:48 -0700 | [diff] [blame] | 152 | """ |
| 153 | Apply the stressor a given number of times. |
Craig Harrison | e3ea8f2 | 2012-10-01 13:55:21 -0700 | [diff] [blame] | 154 | |
Craig Harrison | fc459df | 2012-10-04 13:49:48 -0700 | [diff] [blame] | 155 | Overloaded from parent. |
| 156 | |
Craig Harrison | 922fcb9 | 2012-10-31 09:06:22 -0700 | [diff] [blame^] | 157 | @param iterations: number of times to apply the stressor. |
Craig Harrison | fc459df | 2012-10-04 13:49:48 -0700 | [diff] [blame] | 158 | @param start_condition: the new thread will wait to until this optional |
| 159 | callable returns True before running the stressor. |
Craig Harrison | e3ea8f2 | 2012-10-01 13:55:21 -0700 | [diff] [blame] | 160 | """ |
Craig Harrison | 922fcb9 | 2012-10-31 09:06:22 -0700 | [diff] [blame^] | 161 | self._iterations = iterations |
Craig Harrison | fc459df | 2012-10-04 13:49:48 -0700 | [diff] [blame] | 162 | super(CountedStressor, self).start(start_condition) |
Craig Harrison | e3ea8f2 | 2012-10-01 13:55:21 -0700 | [diff] [blame] | 163 | |
| 164 | |
| 165 | def wait(self, timeout=None): |
| 166 | """Wait until the stressor completes. |
| 167 | |
Craig Harrison | fc459df | 2012-10-04 13:49:48 -0700 | [diff] [blame] | 168 | @param timeout: maximum time for the thread to complete, by default |
| 169 | never times out. |
Craig Harrison | e3ea8f2 | 2012-10-01 13:55:21 -0700 | [diff] [blame] | 170 | """ |
| 171 | self.join(timeout) |
Craig Harrison | 922fcb9 | 2012-10-31 09:06:22 -0700 | [diff] [blame^] | 172 | self.reraise() |