blob: 2b0d5255b1b31258aaacdaae6ae182ea21c733ad [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 Harrison922fcb92012-10-31 09:06:22 -070017 def __init__(self, stressor, 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 Harrison922fcb92012-10-31 09:06:22 -070022 @param escalate_exceptions: whether to escalate exceptions to the parent
23 thread; defaults to True.
Craig Harrisonfc459df2012-10-04 13:49:48 -070024 """
25 super(BaseStressor, self).__init__()
Craig Harrisone3ea8f22012-10-01 13:55:21 -070026 self.daemon = True
Craig Harrisonfc459df2012-10-04 13:49:48 -070027 self.stressor = stressor
Craig Harrison922fcb92012-10-31 09:06:22 -070028 self._escalate_exceptions = escalate_exceptions
29 self._exc_info = None
Craig Harrisonfc459df2012-10-04 13:49:48 -070030
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 Harrison922fcb92012-10-31 09:06:22 -070039 @param start_condition: the new thread will wait until this optional
Craig Harrisonfc459df2012-10-04 13:49:48 -070040 callable returns True before running the stressor.
41 """
42 self._start_condition = start_condition
43 super(BaseStressor, self).start()
Craig Harrisone3ea8f22012-10-01 13:55:21 -070044
45
46 def run(self):
Craig Harrisonfc459df2012-10-04 13:49:48 -070047 """
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 Harrison922fcb92012-10-31 09:06:22 -070056 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 Harrisonfc459df2012-10-04 13:49:48 -070062
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 Harrison922fcb92012-10-31 09:06:22 -070073 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 Harrisonfc459df2012-10-04 13:49:48 -070085class 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 Harrison922fcb92012-10-31 09:06:22 -070091 def __init__(self, stressor, escalate_exceptions=True):
Craig Harrisonfc459df2012-10-04 13:49:48 -070092 """
93 Initialize the ControlledStressor.
94
95 @param stressor: callable which performs a single stress event.
Craig Harrison922fcb92012-10-31 09:06:22 -070096 @param escalate_exceptions: whether to escalate exceptions to the parent
97 thread; defaults to True.
Craig Harrisonfc459df2012-10-04 13:49:48 -070098 """
99 self._complete = threading.Event()
Craig Harrison922fcb92012-10-31 09:06:22 -0700100 super(ControlledStressor, self).__init__(stressor, escalate_exceptions)
Craig Harrisonfc459df2012-10-04 13:49:48 -0700101
102
103 def _loop_stressor(self):
104 """Overloaded from parent."""
Craig Harrison922fcb92012-10-31 09:06:22 -0700105 iteration_num = 0
Craig Harrisone3ea8f22012-10-01 13:55:21 -0700106 while not self._complete.is_set():
Craig Harrison922fcb92012-10-31 09:06:22 -0700107 iteration_num += 1
108 logging.info('Stressor iteration: %d' % iteration_num)
Craig Harrisonfc459df2012-10-04 13:49:48 -0700109 self.stressor()
Craig Harrisone3ea8f22012-10-01 13:55:21 -0700110
111
Craig Harrisonfc459df2012-10-04 13:49:48 -0700112 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 Harrisone3ea8f22012-10-01 13:55:21 -0700120 self._complete.clear()
Craig Harrisonfc459df2012-10-04 13:49:48 -0700121 super(ControlledStressor, self).start(start_condition)
Craig Harrisone3ea8f22012-10-01 13:55:21 -0700122
123
124 def stop(self, timeout=45):
Craig Harrisonfc459df2012-10-04 13:49:48 -0700125 """
126 Stop applying the stressor.
Craig Harrisone3ea8f22012-10-01 13:55:21 -0700127
Craig Harrisonfc459df2012-10-04 13:49:48 -0700128 @param timeout: maximum time to wait for a single run of the stressor to
129 complete, defaults to 45 seconds.
130 """
Craig Harrisone3ea8f22012-10-01 13:55:21 -0700131 self._complete.set()
132 self.join(timeout)
Craig Harrison922fcb92012-10-31 09:06:22 -0700133 self.reraise()
Craig Harrisone3ea8f22012-10-01 13:55:21 -0700134
135
Craig Harrisonfc459df2012-10-04 13:49:48 -0700136class CountedStressor(BaseStressor):
137 """
138 Run a stressor in a loop on a separate thread a given number of times.
Craig Harrisone3ea8f22012-10-01 13:55:21 -0700139
Craig Harrison922fcb92012-10-31 09:06:22 -0700140 Creates a new thread and calls |stressor| in a loop |iterations| times. The
Craig Harrisonfc459df2012-10-04 13:49:48 -0700141 calling thread can use wait() to block until the loop completes.
142 """
143 def _loop_stressor(self):
144 """Overloaded from parent."""
Craig Harrison922fcb92012-10-31 09:06:22 -0700145 for iteration_num in xrange(1, self._iterations + 1):
146 logging.info('Stressor iteration: %d of %d' % (iteration_num,
147 self._iterations))
Craig Harrisonfc459df2012-10-04 13:49:48 -0700148 self.stressor()
Craig Harrisone3ea8f22012-10-01 13:55:21 -0700149
150
Craig Harrison922fcb92012-10-31 09:06:22 -0700151 def start(self, iterations, start_condition=None):
Craig Harrisonfc459df2012-10-04 13:49:48 -0700152 """
153 Apply the stressor a given number of times.
Craig Harrisone3ea8f22012-10-01 13:55:21 -0700154
Craig Harrisonfc459df2012-10-04 13:49:48 -0700155 Overloaded from parent.
156
Craig Harrison922fcb92012-10-31 09:06:22 -0700157 @param iterations: number of times to apply the stressor.
Craig Harrisonfc459df2012-10-04 13:49:48 -0700158 @param start_condition: the new thread will wait to until this optional
159 callable returns True before running the stressor.
Craig Harrisone3ea8f22012-10-01 13:55:21 -0700160 """
Craig Harrison922fcb92012-10-31 09:06:22 -0700161 self._iterations = iterations
Craig Harrisonfc459df2012-10-04 13:49:48 -0700162 super(CountedStressor, self).start(start_condition)
Craig Harrisone3ea8f22012-10-01 13:55:21 -0700163
164
165 def wait(self, timeout=None):
166 """Wait until the stressor completes.
167
Craig Harrisonfc459df2012-10-04 13:49:48 -0700168 @param timeout: maximum time for the thread to complete, by default
169 never times out.
Craig Harrisone3ea8f22012-10-01 13:55:21 -0700170 """
171 self.join(timeout)
Craig Harrison922fcb92012-10-31 09:06:22 -0700172 self.reraise()