blob: f42fa1345eaa383eea241854ba7185b77d7aa66b [file] [log] [blame]
Craig Harrisone3ea8f22012-10-01 13:55:21 -07001# 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 Harrison922fcb92012-10-31 09:06:22 -07005import logging
6import sys
Craig Harrisone3ea8f22012-10-01 13:55:21 -07007import threading
Craig Harrisonfc459df2012-10-04 13:49:48 -07008import time
mukesh agrawal84873472015-05-15 14:37:59 -07009from autotest_lib.client.common_lib import error
Craig Harrisone3ea8f22012-10-01 13:55:21 -070010
11
Craig Harrisonfc459df2012-10-04 13:49:48 -070012class BaseStressor(threading.Thread):
13 """
14 Implements common functionality for *Stressor classes.
15
16 @var stressor: callable which performs a single stress event.
17 """
Craig Harrison9a04f952012-11-20 15:49:46 -080018 def __init__(self, stressor, on_exit=None, escalate_exceptions=True):
Craig Harrisone3ea8f22012-10-01 13:55:21 -070019 """
Craig Harrisonfc459df2012-10-04 13:49:48 -070020 Initialize the ControlledStressor.
21
22 @param stressor: callable which performs a single stress event.
Craig Harrison9a04f952012-11-20 15:49:46 -080023 @param on_exit: callable which will be called when the thread finishes.
Craig Harrison922fcb92012-10-31 09:06:22 -070024 @param escalate_exceptions: whether to escalate exceptions to the parent
25 thread; defaults to True.
Craig Harrisonfc459df2012-10-04 13:49:48 -070026 """
27 super(BaseStressor, self).__init__()
Craig Harrisone3ea8f22012-10-01 13:55:21 -070028 self.daemon = True
Craig Harrisonfc459df2012-10-04 13:49:48 -070029 self.stressor = stressor
Craig Harrison9a04f952012-11-20 15:49:46 -080030 self.on_exit = on_exit
Craig Harrison922fcb92012-10-31 09:06:22 -070031 self._escalate_exceptions = escalate_exceptions
32 self._exc_info = None
Craig Harrisonfc459df2012-10-04 13:49:48 -070033
34
mukesh agrawal84873472015-05-15 14:37:59 -070035 def start(self, start_condition=None, start_timeout_secs=None):
Craig Harrisonfc459df2012-10-04 13:49:48 -070036 """
37 Creates a new thread which will call the run() method.
38
39 Optionally takes a wait condition before the stressor loop. Returns
40 immediately.
41
Craig Harrison922fcb92012-10-31 09:06:22 -070042 @param start_condition: the new thread will wait until this optional
Craig Harrisonfc459df2012-10-04 13:49:48 -070043 callable returns True before running the stressor.
mukesh agrawal84873472015-05-15 14:37:59 -070044 @param start_timeout_secs: how long to wait for |start_condition| to
45 become True, or None to wait forever.
Craig Harrisonfc459df2012-10-04 13:49:48 -070046 """
47 self._start_condition = start_condition
mukesh agrawal84873472015-05-15 14:37:59 -070048 self._start_timeout_secs = start_timeout_secs
Craig Harrisonfc459df2012-10-04 13:49:48 -070049 super(BaseStressor, self).start()
Craig Harrisone3ea8f22012-10-01 13:55:21 -070050
51
52 def run(self):
Craig Harrisonfc459df2012-10-04 13:49:48 -070053 """
mukesh agrawal84873472015-05-15 14:37:59 -070054 Wait for |_start_condition|, and then start the stressor loop.
Craig Harrisonfc459df2012-10-04 13:49:48 -070055
56 Overloaded from threading.Thread. This is run in a separate thread when
57 start() is called.
58 """
Craig Harrison922fcb92012-10-31 09:06:22 -070059 try:
mukesh agrawal84873472015-05-15 14:37:59 -070060 self._wait_for_start_condition()
Craig Harrison922fcb92012-10-31 09:06:22 -070061 self._loop_stressor()
62 except Exception as e:
63 if self._escalate_exceptions:
64 self._exc_info = sys.exc_info()
mukesh agrawal84873472015-05-15 14:37:59 -070065 raise # Terminates this thread. Caller continues to run.
Craig Harrison9a04f952012-11-20 15:49:46 -080066 finally:
67 if self.on_exit:
68 self.on_exit()
Craig Harrisonfc459df2012-10-04 13:49:48 -070069
70
mukesh agrawal84873472015-05-15 14:37:59 -070071 def _wait_for_start_condition(self):
72 """
73 Loop until _start_condition() returns True, or _start_timeout_secs
74 have elapsed.
75
76 @raise error.TestFail if we time out waiting for the start condition
77 """
78 if self._start_condition is None:
79 return
80
81 elapsed_secs = 0
82 while not self._start_condition():
83 if (self._start_timeout_secs and
84 elapsed_secs >= self._start_timeout_secs):
85 raise error.TestFail('start condition did not become true '
86 'within %d seconds' %
87 self._start_timeout_secs)
88 time.sleep(1)
89 elapsed_secs += 1
90
91
Craig Harrisonfc459df2012-10-04 13:49:48 -070092 def _loop_stressor(self):
93 """
94 Apply stressor in a loop.
95
96 Overloaded by the particular *Stressor.
97 """
98 raise NotImplementedError
99
100
Craig Harrison922fcb92012-10-31 09:06:22 -0700101 def reraise(self):
102 """
103 Reraise an exception raised in the thread's stress loop.
104
105 This is a No-op if no exception was raised.
106 """
107 if self._exc_info:
108 exc_info = self._exc_info
109 self._exc_info = None
110 raise exc_info[0], exc_info[1], exc_info[2]
111
112
Craig Harrisonfc459df2012-10-04 13:49:48 -0700113class ControlledStressor(BaseStressor):
114 """
115 Run a stressor in loop on a separate thread.
116
117 Creates a new thread and calls |stressor| in a loop until stop() is called.
118 """
Craig Harrison9a04f952012-11-20 15:49:46 -0800119 def __init__(self, stressor, on_exit=None, escalate_exceptions=True):
Craig Harrisonfc459df2012-10-04 13:49:48 -0700120 """
121 Initialize the ControlledStressor.
122
123 @param stressor: callable which performs a single stress event.
Craig Harrison9a04f952012-11-20 15:49:46 -0800124 @param on_exit: callable which will be called when the thread finishes.
Craig Harrison922fcb92012-10-31 09:06:22 -0700125 @param escalate_exceptions: whether to escalate exceptions to the parent
mukesh agrawal84873472015-05-15 14:37:59 -0700126 thread when stop() is called; defaults to True.
Craig Harrisonfc459df2012-10-04 13:49:48 -0700127 """
128 self._complete = threading.Event()
Craig Harrison9a04f952012-11-20 15:49:46 -0800129 super(ControlledStressor, self).__init__(stressor, on_exit,
130 escalate_exceptions)
Craig Harrisonfc459df2012-10-04 13:49:48 -0700131
132
133 def _loop_stressor(self):
134 """Overloaded from parent."""
Craig Harrison922fcb92012-10-31 09:06:22 -0700135 iteration_num = 0
Craig Harrisone3ea8f22012-10-01 13:55:21 -0700136 while not self._complete.is_set():
Craig Harrison922fcb92012-10-31 09:06:22 -0700137 iteration_num += 1
mukesh agrawal84873472015-05-15 14:37:59 -0700138 logging.info('Stressor iteration: %d', iteration_num)
Craig Harrisonfc459df2012-10-04 13:49:48 -0700139 self.stressor()
Craig Harrisone3ea8f22012-10-01 13:55:21 -0700140
141
mukesh agrawal84873472015-05-15 14:37:59 -0700142 def start(self, start_condition=None, start_timeout_secs=None):
Craig Harrisonfc459df2012-10-04 13:49:48 -0700143 """Start applying the stressor.
144
145 Overloaded from parent.
146
147 @param start_condition: the new thread will wait to until this optional
148 callable returns True before running the stressor.
mukesh agrawal84873472015-05-15 14:37:59 -0700149 @param start_timeout_secs: how long to wait for |start_condition| to
150 become True, or None to wait forever.
Craig Harrisonfc459df2012-10-04 13:49:48 -0700151 """
Craig Harrisone3ea8f22012-10-01 13:55:21 -0700152 self._complete.clear()
mukesh agrawal84873472015-05-15 14:37:59 -0700153 super(ControlledStressor, self).start(start_condition,
154 start_timeout_secs)
Craig Harrisone3ea8f22012-10-01 13:55:21 -0700155
156
157 def stop(self, timeout=45):
Craig Harrisonfc459df2012-10-04 13:49:48 -0700158 """
159 Stop applying the stressor.
Craig Harrisone3ea8f22012-10-01 13:55:21 -0700160
Craig Harrisonfc459df2012-10-04 13:49:48 -0700161 @param timeout: maximum time to wait for a single run of the stressor to
162 complete, defaults to 45 seconds.
163 """
Craig Harrisone3ea8f22012-10-01 13:55:21 -0700164 self._complete.set()
165 self.join(timeout)
Craig Harrison922fcb92012-10-31 09:06:22 -0700166 self.reraise()
Craig Harrisone3ea8f22012-10-01 13:55:21 -0700167
168
Craig Harrisonfc459df2012-10-04 13:49:48 -0700169class CountedStressor(BaseStressor):
170 """
171 Run a stressor in a loop on a separate thread a given number of times.
Craig Harrisone3ea8f22012-10-01 13:55:21 -0700172
Craig Harrison922fcb92012-10-31 09:06:22 -0700173 Creates a new thread and calls |stressor| in a loop |iterations| times. The
mukesh agrawal84873472015-05-15 14:37:59 -0700174 calling thread can use wait() to block until the loop completes. If the
175 stressor thread terminates with an exception, wait() will propagate that
176 exception to the thread that called wait().
Craig Harrisonfc459df2012-10-04 13:49:48 -0700177 """
178 def _loop_stressor(self):
179 """Overloaded from parent."""
Craig Harrison922fcb92012-10-31 09:06:22 -0700180 for iteration_num in xrange(1, self._iterations + 1):
mukesh agrawal84873472015-05-15 14:37:59 -0700181 logging.info('Stressor iteration: %d of %d',
182 iteration_num, self._iterations)
Craig Harrisonfc459df2012-10-04 13:49:48 -0700183 self.stressor()
Craig Harrisone3ea8f22012-10-01 13:55:21 -0700184
185
mukesh agrawal84873472015-05-15 14:37:59 -0700186 def start(self, iterations, start_condition=None, start_timeout_secs=None):
Craig Harrisonfc459df2012-10-04 13:49:48 -0700187 """
188 Apply the stressor a given number of times.
Craig Harrisone3ea8f22012-10-01 13:55:21 -0700189
Craig Harrisonfc459df2012-10-04 13:49:48 -0700190 Overloaded from parent.
191
Craig Harrison922fcb92012-10-31 09:06:22 -0700192 @param iterations: number of times to apply the stressor.
Craig Harrisonfc459df2012-10-04 13:49:48 -0700193 @param start_condition: the new thread will wait to until this optional
194 callable returns True before running the stressor.
mukesh agrawal84873472015-05-15 14:37:59 -0700195 @param start_timeout_secs: how long to wait for |start_condition| to
196 become True, or None to wait forever.
Craig Harrisone3ea8f22012-10-01 13:55:21 -0700197 """
Craig Harrison922fcb92012-10-31 09:06:22 -0700198 self._iterations = iterations
mukesh agrawal84873472015-05-15 14:37:59 -0700199 super(CountedStressor, self).start(start_condition, start_timeout_secs)
Craig Harrisone3ea8f22012-10-01 13:55:21 -0700200
201
202 def wait(self, timeout=None):
203 """Wait until the stressor completes.
204
Craig Harrisonfc459df2012-10-04 13:49:48 -0700205 @param timeout: maximum time for the thread to complete, by default
206 never times out.
Craig Harrisone3ea8f22012-10-01 13:55:21 -0700207 """
208 self.join(timeout)
Craig Harrison922fcb92012-10-31 09:06:22 -0700209 self.reraise()