blob: 10495c91a319f5bce5fc2aa261d021473565681a [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
Craig Harrisone3ea8f22012-10-01 13:55:21 -07009
10
Craig Harrisonfc459df2012-10-04 13:49:48 -070011class BaseStressor(threading.Thread):
12 """
13 Implements common functionality for *Stressor classes.
14
15 @var stressor: callable which performs a single stress event.
16 """
Craig Harrison9a04f952012-11-20 15:49:46 -080017 def __init__(self, stressor, on_exit=None, escalate_exceptions=True):
Craig Harrisone3ea8f22012-10-01 13:55:21 -070018 """
Craig Harrisonfc459df2012-10-04 13:49:48 -070019 Initialize the ControlledStressor.
20
21 @param stressor: callable which performs a single stress event.
Craig Harrison9a04f952012-11-20 15:49:46 -080022 @param on_exit: callable which will be called when the thread finishes.
Craig Harrison922fcb92012-10-31 09:06:22 -070023 @param escalate_exceptions: whether to escalate exceptions to the parent
24 thread; defaults to True.
Craig Harrisonfc459df2012-10-04 13:49:48 -070025 """
26 super(BaseStressor, self).__init__()
Craig Harrisone3ea8f22012-10-01 13:55:21 -070027 self.daemon = True
Craig Harrisonfc459df2012-10-04 13:49:48 -070028 self.stressor = stressor
Craig Harrison9a04f952012-11-20 15:49:46 -080029 self.on_exit = on_exit
Craig Harrison922fcb92012-10-31 09:06:22 -070030 self._escalate_exceptions = escalate_exceptions
31 self._exc_info = None
Craig Harrisonfc459df2012-10-04 13:49:48 -070032
33
34 def start(self, start_condition=None):
35 """
36 Creates a new thread which will call the run() method.
37
38 Optionally takes a wait condition before the stressor loop. Returns
39 immediately.
40
Craig Harrison922fcb92012-10-31 09:06:22 -070041 @param start_condition: the new thread will wait until this optional
Craig Harrisonfc459df2012-10-04 13:49:48 -070042 callable returns True before running the stressor.
43 """
44 self._start_condition = start_condition
45 super(BaseStressor, self).start()
Craig Harrisone3ea8f22012-10-01 13:55:21 -070046
47
48 def run(self):
Craig Harrisonfc459df2012-10-04 13:49:48 -070049 """
50 Introduce a delay then start the stressor loop.
51
52 Overloaded from threading.Thread. This is run in a separate thread when
53 start() is called.
54 """
55 if self._start_condition:
56 while not self._start_condition():
57 time.sleep(1)
Craig Harrison922fcb92012-10-31 09:06:22 -070058 try:
59 self._loop_stressor()
60 except Exception as e:
61 if self._escalate_exceptions:
62 self._exc_info = sys.exc_info()
63 raise
Craig Harrison9a04f952012-11-20 15:49:46 -080064 finally:
65 if self.on_exit:
66 self.on_exit()
Craig Harrisonfc459df2012-10-04 13:49:48 -070067
68
69 def _loop_stressor(self):
70 """
71 Apply stressor in a loop.
72
73 Overloaded by the particular *Stressor.
74 """
75 raise NotImplementedError
76
77
Craig Harrison922fcb92012-10-31 09:06:22 -070078 def reraise(self):
79 """
80 Reraise an exception raised in the thread's stress loop.
81
82 This is a No-op if no exception was raised.
83 """
84 if self._exc_info:
85 exc_info = self._exc_info
86 self._exc_info = None
87 raise exc_info[0], exc_info[1], exc_info[2]
88
89
Craig Harrisonfc459df2012-10-04 13:49:48 -070090class ControlledStressor(BaseStressor):
91 """
92 Run a stressor in loop on a separate thread.
93
94 Creates a new thread and calls |stressor| in a loop until stop() is called.
95 """
Craig Harrison9a04f952012-11-20 15:49:46 -080096 def __init__(self, stressor, on_exit=None, escalate_exceptions=True):
Craig Harrisonfc459df2012-10-04 13:49:48 -070097 """
98 Initialize the ControlledStressor.
99
100 @param stressor: callable which performs a single stress event.
Craig Harrison9a04f952012-11-20 15:49:46 -0800101 @param on_exit: callable which will be called when the thread finishes.
Craig Harrison922fcb92012-10-31 09:06:22 -0700102 @param escalate_exceptions: whether to escalate exceptions to the parent
103 thread; defaults to True.
Craig Harrisonfc459df2012-10-04 13:49:48 -0700104 """
105 self._complete = threading.Event()
Craig Harrison9a04f952012-11-20 15:49:46 -0800106 super(ControlledStressor, self).__init__(stressor, on_exit,
107 escalate_exceptions)
Craig Harrisonfc459df2012-10-04 13:49:48 -0700108
109
110 def _loop_stressor(self):
111 """Overloaded from parent."""
Craig Harrison922fcb92012-10-31 09:06:22 -0700112 iteration_num = 0
Craig Harrisone3ea8f22012-10-01 13:55:21 -0700113 while not self._complete.is_set():
Craig Harrison922fcb92012-10-31 09:06:22 -0700114 iteration_num += 1
115 logging.info('Stressor iteration: %d' % iteration_num)
Craig Harrisonfc459df2012-10-04 13:49:48 -0700116 self.stressor()
Craig Harrisone3ea8f22012-10-01 13:55:21 -0700117
118
Craig Harrisonfc459df2012-10-04 13:49:48 -0700119 def start(self, start_condition=None):
120 """Start applying the stressor.
121
122 Overloaded from parent.
123
124 @param start_condition: the new thread will wait to until this optional
125 callable returns True before running the stressor.
126 """
Craig Harrisone3ea8f22012-10-01 13:55:21 -0700127 self._complete.clear()
Craig Harrisonfc459df2012-10-04 13:49:48 -0700128 super(ControlledStressor, self).start(start_condition)
Craig Harrisone3ea8f22012-10-01 13:55:21 -0700129
130
131 def stop(self, timeout=45):
Craig Harrisonfc459df2012-10-04 13:49:48 -0700132 """
133 Stop applying the stressor.
Craig Harrisone3ea8f22012-10-01 13:55:21 -0700134
Craig Harrisonfc459df2012-10-04 13:49:48 -0700135 @param timeout: maximum time to wait for a single run of the stressor to
136 complete, defaults to 45 seconds.
137 """
Craig Harrisone3ea8f22012-10-01 13:55:21 -0700138 self._complete.set()
139 self.join(timeout)
Craig Harrison922fcb92012-10-31 09:06:22 -0700140 self.reraise()
Craig Harrisone3ea8f22012-10-01 13:55:21 -0700141
142
Craig Harrisonfc459df2012-10-04 13:49:48 -0700143class CountedStressor(BaseStressor):
144 """
145 Run a stressor in a loop on a separate thread a given number of times.
Craig Harrisone3ea8f22012-10-01 13:55:21 -0700146
Craig Harrison922fcb92012-10-31 09:06:22 -0700147 Creates a new thread and calls |stressor| in a loop |iterations| times. The
Craig Harrisonfc459df2012-10-04 13:49:48 -0700148 calling thread can use wait() to block until the loop completes.
149 """
150 def _loop_stressor(self):
151 """Overloaded from parent."""
Craig Harrison922fcb92012-10-31 09:06:22 -0700152 for iteration_num in xrange(1, self._iterations + 1):
153 logging.info('Stressor iteration: %d of %d' % (iteration_num,
154 self._iterations))
Craig Harrisonfc459df2012-10-04 13:49:48 -0700155 self.stressor()
Craig Harrisone3ea8f22012-10-01 13:55:21 -0700156
157
Craig Harrison922fcb92012-10-31 09:06:22 -0700158 def start(self, iterations, start_condition=None):
Craig Harrisonfc459df2012-10-04 13:49:48 -0700159 """
160 Apply the stressor a given number of times.
Craig Harrisone3ea8f22012-10-01 13:55:21 -0700161
Craig Harrisonfc459df2012-10-04 13:49:48 -0700162 Overloaded from parent.
163
Craig Harrison922fcb92012-10-31 09:06:22 -0700164 @param iterations: number of times to apply the stressor.
Craig Harrisonfc459df2012-10-04 13:49:48 -0700165 @param start_condition: the new thread will wait to until this optional
166 callable returns True before running the stressor.
Craig Harrisone3ea8f22012-10-01 13:55:21 -0700167 """
Craig Harrison922fcb92012-10-31 09:06:22 -0700168 self._iterations = iterations
Craig Harrisonfc459df2012-10-04 13:49:48 -0700169 super(CountedStressor, self).start(start_condition)
Craig Harrisone3ea8f22012-10-01 13:55:21 -0700170
171
172 def wait(self, timeout=None):
173 """Wait until the stressor completes.
174
Craig Harrisonfc459df2012-10-04 13:49:48 -0700175 @param timeout: maximum time for the thread to complete, by default
176 never times out.
Craig Harrisone3ea8f22012-10-01 13:55:21 -0700177 """
178 self.join(timeout)
Craig Harrison922fcb92012-10-31 09:06:22 -0700179 self.reraise()