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