blob: c294a3861a8e36e55d926b82ce63f68d2ed0f1fd [file] [log] [blame]
Brian Carlstrom7c6deaa2011-10-21 12:05:06 -07001/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17import java.util.ArrayList;
18import java.util.Arrays;
19import java.util.Collections;
20import java.util.HashMap;
21import java.util.List;
22import java.util.Map;
23
24// Run on host with:
25// javac ThreadTest.java && java ThreadStress && rm *.class
26class ThreadStress implements Runnable {
27
Brian Carlstrom4514d3c2011-10-21 17:01:31 -070028 public static final boolean DEBUG = false;
Brian Carlstrom7c6deaa2011-10-21 12:05:06 -070029
30 enum Operation {
31 OOM(1),
32 ALLOC(99),
33 EXIT(50),
34 WAIT(50);
35
36 private final int frequency;
37 Operation(int frequency) {
38 this.frequency = frequency;
39 }
40 }
41
42 public static void main(String[] args) throws Exception {
43
44 final int numberOfThreads = 5;
45 final int totalOperations = 1000;
46 final int operationsPerThread = totalOperations/numberOfThreads;
47
48 // Lock used to notify threads performin Operation.WAIT
49 final Object lock = new Object();
50
51 // Each thread is going to do operationsPerThread
52 // operations. The distribution of operations is determined by
53 // the Operation.frequency values. We fill out an Operation[]
54 // for each thread with the operations it is to perform. The
55 // Operation[] is shuffled so that there is more random
56 // interactions between the threads.
57
58 // The simple-minded filling in of Operation[] based on
59 // Operation.frequency below won't have even have close to a
60 // reasonable distribution if the count of Operation
61 // frequencies is greater than the total number of
62 // operations. So here we do a quick sanity check in case
63 // people tweak the constants above.
64 int operationCount = 0;
65 for (Operation op : Operation.values()) {
66 operationCount += op.frequency;
67 }
68 if (operationCount > operationsPerThread) {
69 throw new AssertionError(operationCount + " > " + operationsPerThread);
70 }
71
72 // Fill in the Operation[] array for each thread by laying
73 // down references to operation according to their desired
74 // frequency.
75 final ThreadStress[] threadStresses = new ThreadStress[numberOfThreads];
76 for (int t = 0; t < threadStresses.length; t++) {
77 Operation[] operations = new Operation[operationsPerThread];
78 int o = 0;
79 LOOP:
80 while (true) {
81 for (Operation op : Operation.values()) {
82 for (int f = 0; f < op.frequency; f++) {
83 if (o == operations.length) {
84 break LOOP;
85 }
86 operations[o] = op;
87 o++;
88 }
89 }
90 }
91 // Randomize the oepration order
92 Collections.shuffle(Arrays.asList(operations));
93 threadStresses[t] = new ThreadStress(lock, t, operations);
94 }
95
96 // Enable to dump operation counds per thread to make sure its
97 // sane compared to Operation.frequency
98 if (DEBUG) {
99 for (int t = 0; t < threadStresses.length; t++) {
100 Operation[] operations = new Operation[operationsPerThread];
101 Map<Operation, Integer> distribution = new HashMap<Operation, Integer>();
102 for (Operation operation : operations) {
103 Integer ops = distribution.get(operation);
104 if (ops == null) {
105 ops = 1;
106 } else {
107 ops++;
108 }
109 distribution.put(operation, ops);
110 }
111 System.out.println("Distribution for " + t);
112 for (Operation op : Operation.values()) {
113 System.out.println(op + " = " + distribution.get(op));
114 }
115 }
116 }
117
118 // Create the runners for each thread. The runner Thread
119 // ensures that thread that exit due to Operation.EXIT will be
120 // restarted until they reach their desired
121 // operationsPerThread.
122 Thread[] runners = new Thread[numberOfThreads];
123 for (int r = 0; r < runners.length; r++) {
124 final ThreadStress ts = threadStresses[r];
125 runners[r] = new Thread() {
126 final ThreadStress threadStress = ts;
127 public void run() {
128 int id = threadStress.id;
129 System.out.println("Starting runner for " + id);
130 while (threadStress.nextOperation < operationsPerThread) {
131 Thread thread = new Thread(ts);
132 thread.start();
133 try {
134 thread.join();
135 } catch (InterruptedException e) {
136 }
137 System.out.println("Thread exited for " + id + " with "
138 + (operationsPerThread - threadStress.nextOperation)
139 + " operations remaining.");
140 }
141 System.out.println("Finishing runner for " + id);
142 }
143 };
144 }
145
146 // The notifier thread is a daemon just loops forever to wake
147 // up threads in Operation.WAIT
148 Thread notifier = new Thread() {
149 public void run() {
150 while (true) {
151 synchronized (lock) {
152 lock.notifyAll();
153 }
154 }
155 }
156 };
157 notifier.setDaemon(true);
158 notifier.start();
159
160 for (int r = 0; r < runners.length; r++) {
161 runners[r].start();
162 }
163 for (int r = 0; r < runners.length; r++) {
164 runners[r].join();
165 }
166 }
167
168 private final Operation[] operations;
169 private final Object lock;
170 private final int id;
171
172 private int nextOperation;
173
174 private ThreadStress(Object lock, int id, Operation[] operations) {
175 this.lock = lock;
176 this.id = id;
177 this.operations = operations;
178 }
179
180 public void run() {
181 try {
182 if (DEBUG) {
183 System.out.println("Starting ThreadStress " + id);
184 }
185 while (nextOperation < operations.length) {
186 Operation operation = operations[nextOperation];
187 if (DEBUG) {
188 System.out.println("ThreadStress " + id
189 + " operation " + nextOperation
190 + " is " + operation);
191 }
192 nextOperation++;
193 switch (operation) {
194 case EXIT: {
195 return;
196 }
197 case WAIT: {
198 synchronized (lock) {
199 try {
200 lock.wait();
201 } catch (InterruptedException e) {
202 }
203 }
204 break;
205 }
206 case OOM: {
207 try {
208 List<byte[]> l = new ArrayList<byte[]>();
209 while (true) {
210 l.add(new byte[1024]);
211 }
212 } catch (OutOfMemoryError e) {
213 }
214 break;
215 }
216 case ALLOC: {
Brian Carlstrom4514d3c2011-10-21 17:01:31 -0700217 try {
218 List<byte[]> l = new ArrayList<byte[]>();
219 for (int i = 0; i < 1024; i++) {
220 l.add(new byte[1024]);
221 }
222 } catch (OutOfMemoryError e) {
Brian Carlstrom7c6deaa2011-10-21 12:05:06 -0700223 }
224 break;
225 }
226 default: {
227 throw new AssertionError(operation.toString());
228 }
229 }
230 }
Brian Carlstrom4514d3c2011-10-21 17:01:31 -0700231 } finally {
Brian Carlstrom7c6deaa2011-10-21 12:05:06 -0700232 if (DEBUG) {
233 System.out.println("Finishing ThreadStress for " + id);
234 }
235 }
236 }
237}