blob: 1f8fb2d9224dfc55c62b165a58fdfd7695648e44 [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;
Elliott Hughes831afe42011-12-15 17:27:34 -080023import libcore.io.*;
Brian Carlstrom7c6deaa2011-10-21 12:05:06 -070024
25// Run on host with:
26// javac ThreadTest.java && java ThreadStress && rm *.class
27class ThreadStress implements Runnable {
28
Brian Carlstrom4514d3c2011-10-21 17:01:31 -070029 public static final boolean DEBUG = false;
Brian Carlstrom7c6deaa2011-10-21 12:05:06 -070030
31 enum Operation {
32 OOM(1),
Elliott Hughes831afe42011-12-15 17:27:34 -080033 SIGQUIT(19),
34 ALLOC(80),
Brian Carlstrom7c6deaa2011-10-21 12:05:06 -070035 EXIT(50),
36 WAIT(50);
37
38 private final int frequency;
39 Operation(int frequency) {
40 this.frequency = frequency;
41 }
42 }
43
44 public static void main(String[] args) throws Exception {
45
46 final int numberOfThreads = 5;
47 final int totalOperations = 1000;
48 final int operationsPerThread = totalOperations/numberOfThreads;
49
50 // Lock used to notify threads performin Operation.WAIT
51 final Object lock = new Object();
52
53 // Each thread is going to do operationsPerThread
54 // operations. The distribution of operations is determined by
55 // the Operation.frequency values. We fill out an Operation[]
56 // for each thread with the operations it is to perform. The
57 // Operation[] is shuffled so that there is more random
58 // interactions between the threads.
59
60 // The simple-minded filling in of Operation[] based on
61 // Operation.frequency below won't have even have close to a
62 // reasonable distribution if the count of Operation
63 // frequencies is greater than the total number of
64 // operations. So here we do a quick sanity check in case
65 // people tweak the constants above.
66 int operationCount = 0;
67 for (Operation op : Operation.values()) {
68 operationCount += op.frequency;
69 }
70 if (operationCount > operationsPerThread) {
71 throw new AssertionError(operationCount + " > " + operationsPerThread);
72 }
73
74 // Fill in the Operation[] array for each thread by laying
75 // down references to operation according to their desired
76 // frequency.
77 final ThreadStress[] threadStresses = new ThreadStress[numberOfThreads];
78 for (int t = 0; t < threadStresses.length; t++) {
79 Operation[] operations = new Operation[operationsPerThread];
80 int o = 0;
81 LOOP:
82 while (true) {
83 for (Operation op : Operation.values()) {
84 for (int f = 0; f < op.frequency; f++) {
85 if (o == operations.length) {
86 break LOOP;
87 }
88 operations[o] = op;
89 o++;
90 }
91 }
92 }
93 // Randomize the oepration order
94 Collections.shuffle(Arrays.asList(operations));
95 threadStresses[t] = new ThreadStress(lock, t, operations);
96 }
97
98 // Enable to dump operation counds per thread to make sure its
99 // sane compared to Operation.frequency
100 if (DEBUG) {
101 for (int t = 0; t < threadStresses.length; t++) {
102 Operation[] operations = new Operation[operationsPerThread];
103 Map<Operation, Integer> distribution = new HashMap<Operation, Integer>();
104 for (Operation operation : operations) {
105 Integer ops = distribution.get(operation);
106 if (ops == null) {
107 ops = 1;
108 } else {
109 ops++;
110 }
111 distribution.put(operation, ops);
112 }
113 System.out.println("Distribution for " + t);
114 for (Operation op : Operation.values()) {
115 System.out.println(op + " = " + distribution.get(op));
116 }
117 }
118 }
119
120 // Create the runners for each thread. The runner Thread
121 // ensures that thread that exit due to Operation.EXIT will be
122 // restarted until they reach their desired
123 // operationsPerThread.
124 Thread[] runners = new Thread[numberOfThreads];
125 for (int r = 0; r < runners.length; r++) {
126 final ThreadStress ts = threadStresses[r];
127 runners[r] = new Thread() {
128 final ThreadStress threadStress = ts;
129 public void run() {
130 int id = threadStress.id;
131 System.out.println("Starting runner for " + id);
132 while (threadStress.nextOperation < operationsPerThread) {
133 Thread thread = new Thread(ts);
134 thread.start();
135 try {
136 thread.join();
137 } catch (InterruptedException e) {
138 }
139 System.out.println("Thread exited for " + id + " with "
140 + (operationsPerThread - threadStress.nextOperation)
141 + " operations remaining.");
142 }
143 System.out.println("Finishing runner for " + id);
144 }
145 };
146 }
147
148 // The notifier thread is a daemon just loops forever to wake
149 // up threads in Operation.WAIT
150 Thread notifier = new Thread() {
151 public void run() {
152 while (true) {
153 synchronized (lock) {
154 lock.notifyAll();
155 }
156 }
157 }
158 };
159 notifier.setDaemon(true);
160 notifier.start();
161
162 for (int r = 0; r < runners.length; r++) {
163 runners[r].start();
164 }
165 for (int r = 0; r < runners.length; r++) {
166 runners[r].join();
167 }
168 }
169
170 private final Operation[] operations;
171 private final Object lock;
172 private final int id;
173
174 private int nextOperation;
175
176 private ThreadStress(Object lock, int id, Operation[] operations) {
177 this.lock = lock;
178 this.id = id;
179 this.operations = operations;
180 }
181
182 public void run() {
183 try {
184 if (DEBUG) {
185 System.out.println("Starting ThreadStress " + id);
186 }
187 while (nextOperation < operations.length) {
188 Operation operation = operations[nextOperation];
189 if (DEBUG) {
190 System.out.println("ThreadStress " + id
191 + " operation " + nextOperation
192 + " is " + operation);
193 }
194 nextOperation++;
195 switch (operation) {
196 case EXIT: {
197 return;
198 }
Elliott Hughes831afe42011-12-15 17:27:34 -0800199 case SIGQUIT: {
200 try {
201 Libcore.os.kill(Libcore.os.getpid(), OsConstants.SIGQUIT);
202 } catch (ErrnoException ex) {
203 }
204 }
Brian Carlstrom7c6deaa2011-10-21 12:05:06 -0700205 case WAIT: {
206 synchronized (lock) {
207 try {
208 lock.wait();
209 } catch (InterruptedException e) {
210 }
211 }
212 break;
213 }
214 case OOM: {
215 try {
216 List<byte[]> l = new ArrayList<byte[]>();
217 while (true) {
218 l.add(new byte[1024]);
219 }
220 } catch (OutOfMemoryError e) {
221 }
222 break;
223 }
224 case ALLOC: {
Brian Carlstrom4514d3c2011-10-21 17:01:31 -0700225 try {
226 List<byte[]> l = new ArrayList<byte[]>();
227 for (int i = 0; i < 1024; i++) {
228 l.add(new byte[1024]);
229 }
230 } catch (OutOfMemoryError e) {
Brian Carlstrom7c6deaa2011-10-21 12:05:06 -0700231 }
232 break;
233 }
234 default: {
235 throw new AssertionError(operation.toString());
236 }
237 }
238 }
Brian Carlstrom4514d3c2011-10-21 17:01:31 -0700239 } finally {
Brian Carlstrom7c6deaa2011-10-21 12:05:06 -0700240 if (DEBUG) {
241 System.out.println("Finishing ThreadStress for " + id);
242 }
243 }
244 }
245}