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 dalvik.system.VMRuntime;
18 
19 import java.lang.reflect.*;
20 import java.util.ArrayList;
21 import java.util.Arrays;
22 import java.util.Collections;
23 import java.util.HashMap;
24 import java.util.HashSet;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Set;
28 import java.util.concurrent.Semaphore;
29 
30 // Run on host with:
31 //   javac ThreadTest.java && java ThreadStress && rm *.class
32 // Through run-test:
33 //   test/run-test {run-test-args} 004-ThreadStress [Main {ThreadStress-args}]
34 //   (It is important to pass Main if you want to give parameters...)
35 //
36 // ThreadStress command line parameters:
37 //    -n X .............. number of threads
38 //    -d X .............. number of daemon threads
39 //    -o X .............. number of overall operations
40 //    -t X .............. number of operations per thread
41 //    -p X .............. number of permits granted by semaphore
42 //    --dumpmap ......... print the frequency map
43 //    --locks-only ...... select a pre-set frequency map with lock-related operations only
44 //    --allocs-only ..... select a pre-set frequency map with allocation-related operations only
45 //    -oom:X ............ frequency of OOM (double)
46 //    -sigquit:X ........ frequency of SigQuit (double)
47 //    -alloc:X .......... frequency of Alloc (double)
48 //    -largealloc:X ..... frequency of LargeAlloc (double)
49 //    -nonmovingalloc:X.. frequency of NonMovingAlloc (double)
50 //    -stacktrace:X ..... frequency of StackTrace (double)
51 //    -exit:X ........... frequency of Exit (double)
52 //    -sleep:X .......... frequency of Sleep (double)
53 //    -wait:X ........... frequency of Wait (double)
54 //    -timedwait:X ...... frequency of TimedWait (double)
55 //    -syncandwork:X .... frequency of SyncAndWork (double)
56 //    -queuedwait:X ..... frequency of QueuedWait (double)
57 
58 public class Main implements Runnable {
59 
60     public static final boolean DEBUG = false;
61 
62     private static abstract class Operation {
63         /**
64          * Perform the action represented by this operation. Returns true if the thread should
65          * continue when executed by a runner (non-daemon) thread.
66          */
perform()67         public abstract boolean perform();
68     }
69 
70     private final static class OOM extends Operation {
71         private final static int ALLOC_SIZE = 1024;
72 
73         @Override
perform()74         public boolean perform() {
75             try {
76                 List<byte[]> l = new ArrayList<byte[]>();
77                 while (true) {
78                     l.add(new byte[ALLOC_SIZE]);
79                 }
80             } catch (OutOfMemoryError e) {
81             }
82             return true;
83         }
84     }
85 
86     private final static class SigQuit extends Operation {
87         private final static int sigquit;
88         private final static Method kill;
89         private final static int pid;
90 
91         static {
92             int pidTemp = -1;
93             int sigquitTemp = -1;
94             Method killTemp = null;
95 
96             try {
97                 Class<?> osClass = Class.forName("android.system.Os");
98                 Method getpid = osClass.getDeclaredMethod("getpid");
99                 pidTemp = (Integer)getpid.invoke(null);
100 
101                 Class<?> osConstants = Class.forName("android.system.OsConstants");
102                 Field sigquitField = osConstants.getDeclaredField("SIGQUIT");
103                 sigquitTemp = (Integer)sigquitField.get(null);
104 
105                 killTemp = osClass.getDeclaredMethod("kill", int.class, int.class);
106             } catch (Exception e) {
107                 Main.printThrowable(e);
108             }
109 
110             pid = pidTemp;
111             sigquit = sigquitTemp;
112             kill = killTemp;
113         }
114 
115         @Override
perform()116         public boolean perform() {
117             try {
118                 kill.invoke(null, pid, sigquit);
119             } catch (OutOfMemoryError e) {
120             } catch (Exception e) {
121                 if (!e.getClass().getName().equals(Main.errnoExceptionName)) {
122                     Main.printThrowable(e);
123                 }
124             }
125             return true;
126         }
127     }
128 
129     private final static class Alloc extends Operation {
130         private final static int ALLOC_SIZE = 1024;  // Needs to be small enough to not be in LOS.
131         private final static int ALLOC_COUNT = 1024;
132 
133         @Override
perform()134         public boolean perform() {
135             try {
136                 List<byte[]> l = new ArrayList<byte[]>();
137                 for (int i = 0; i < ALLOC_COUNT; i++) {
138                     l.add(new byte[ALLOC_SIZE]);
139                 }
140             } catch (OutOfMemoryError e) {
141             }
142             return true;
143         }
144     }
145 
146     private final static class LargeAlloc extends Operation {
147         private final static int PAGE_SIZE = 4096;
148         private final static int PAGE_SIZE_MODIFIER = 10;  // Needs to be large enough for LOS.
149         private final static int ALLOC_COUNT = 100;
150 
151         @Override
perform()152         public boolean perform() {
153             try {
154                 List<byte[]> l = new ArrayList<byte[]>();
155                 for (int i = 0; i < ALLOC_COUNT; i++) {
156                     l.add(new byte[PAGE_SIZE_MODIFIER * PAGE_SIZE]);
157                 }
158             } catch (OutOfMemoryError e) {
159             }
160             return true;
161         }
162     }
163 
164   private final static class NonMovingAlloc extends Operation {
165         private final static int ALLOC_SIZE = 1024;  // Needs to be small enough to not be in LOS.
166         private final static int ALLOC_COUNT = 1024;
167         private final static VMRuntime runtime = VMRuntime.getRuntime();
168 
169         @Override
perform()170         public boolean perform() {
171             try {
172                 List<byte[]> l = new ArrayList<byte[]>();
173                 for (int i = 0; i < ALLOC_COUNT; i++) {
174                     l.add((byte[]) runtime.newNonMovableArray(byte.class, ALLOC_SIZE));
175                 }
176             } catch (OutOfMemoryError e) {
177             }
178             return true;
179         }
180     }
181 
182 
183     private final static class StackTrace extends Operation {
184         @Override
perform()185         public boolean perform() {
186             try {
187                 Thread.currentThread().getStackTrace();
188             } catch (OutOfMemoryError e) {
189             }
190             return true;
191         }
192     }
193 
194     private final static class Exit extends Operation {
195         @Override
perform()196         public boolean perform() {
197             return false;
198         }
199     }
200 
201     private final static class Sleep extends Operation {
202         private final static int SLEEP_TIME = 100;
203 
204         @Override
perform()205         public boolean perform() {
206             try {
207                 Thread.sleep(SLEEP_TIME);
208             } catch (InterruptedException ignored) {
209             }
210             return true;
211         }
212     }
213 
214     private final static class TimedWait extends Operation {
215         private final static int SLEEP_TIME = 100;
216 
217         private final Object lock;
218 
TimedWait(Object lock)219         public TimedWait(Object lock) {
220             this.lock = lock;
221         }
222 
223         @Override
perform()224         public boolean perform() {
225             synchronized (lock) {
226                 try {
227                     lock.wait(SLEEP_TIME, 0);
228                 } catch (InterruptedException ignored) {
229                 }
230             }
231             return true;
232         }
233     }
234 
235     private final static class Wait extends Operation {
236         private final Object lock;
237 
Wait(Object lock)238         public Wait(Object lock) {
239             this.lock = lock;
240         }
241 
242         @Override
perform()243         public boolean perform() {
244             synchronized (lock) {
245                 try {
246                     lock.wait();
247                 } catch (InterruptedException ignored) {
248                 }
249             }
250             return true;
251         }
252     }
253 
254     private final static class SyncAndWork extends Operation {
255         private final Object lock;
256 
SyncAndWork(Object lock)257         public SyncAndWork(Object lock) {
258             this.lock = lock;
259         }
260 
261         @Override
perform()262         public boolean perform() {
263             synchronized (lock) {
264                 try {
265                     Thread.sleep((int)(Math.random() * 50 + 50));
266                 } catch (InterruptedException ignored) {
267                 }
268             }
269             return true;
270         }
271     }
272 
273     // An operation requiring the acquisition of a permit from a semaphore
274     // for its execution. This operation has been added to exercise
275     // java.util.concurrent.locks.AbstractQueuedSynchronizer, used in the
276     // implementation of java.util.concurrent.Semaphore. We use the latter,
277     // as the former is not supposed to be used directly (see b/63822989).
278     private final static class QueuedWait extends Operation {
279         private final static int SLEEP_TIME = 100;
280 
281         private final Semaphore semaphore;
282 
QueuedWait(Semaphore semaphore)283         public QueuedWait(Semaphore semaphore) {
284             this.semaphore = semaphore;
285         }
286 
287         @Override
perform()288         public boolean perform() {
289             boolean permitAcquired = false;
290             try {
291                 semaphore.acquire();
292                 permitAcquired = true;
293                 Thread.sleep(SLEEP_TIME);
294             } catch (OutOfMemoryError ignored) {
295               // The call to semaphore.acquire() above may trigger an OOME,
296               // despite the care taken doing some warm-up by forcing
297               // ahead-of-time initialization of classes used by the Semaphore
298               // class (see forceTransitiveClassInitialization below).
299               // For instance, one of the code paths executes
300               // AbstractQueuedSynchronizer.addWaiter, which allocates an
301               // AbstractQueuedSynchronizer$Node (see b/67730573).
302               // In that case, just ignore the OOME and continue.
303             } catch (InterruptedException ignored) {
304             } finally {
305                 if (permitAcquired) {
306                     semaphore.release();
307                 }
308             }
309             return true;
310         }
311     }
312 
createDefaultFrequencyMap(Object lock, Semaphore semaphore)313     private final static Map<Operation, Double> createDefaultFrequencyMap(Object lock,
314             Semaphore semaphore) {
315         Map<Operation, Double> frequencyMap = new HashMap<Operation, Double>();
316         frequencyMap.put(new OOM(), 0.005);                   //   1/200
317         frequencyMap.put(new SigQuit(), 0.095);               //  19/200
318         frequencyMap.put(new Alloc(), 0.225);                 //  45/200
319         frequencyMap.put(new LargeAlloc(), 0.05);             //  10/200
320         // TODO: NonMovingAlloc operations fail an assertion with the
321         // GSS collector (see b/72738921); disable them for now.
322         frequencyMap.put(new NonMovingAlloc(), 0.0);          //   0/200
323         frequencyMap.put(new StackTrace(), 0.1);              //  20/200
324         frequencyMap.put(new Exit(), 0.225);                  //  45/200
325         frequencyMap.put(new Sleep(), 0.125);                 //  25/200
326         frequencyMap.put(new TimedWait(lock), 0.05);          //  10/200
327         frequencyMap.put(new Wait(lock), 0.075);              //  15/200
328         frequencyMap.put(new QueuedWait(semaphore), 0.05);    //  10/200
329 
330         return frequencyMap;
331     }
332 
createAllocFrequencyMap()333     private final static Map<Operation, Double> createAllocFrequencyMap() {
334         Map<Operation, Double> frequencyMap = new HashMap<Operation, Double>();
335         frequencyMap.put(new Sleep(), 0.2);                   //  40/200
336         frequencyMap.put(new Alloc(), 0.575);                 // 115/200
337         frequencyMap.put(new LargeAlloc(), 0.15);             //  30/200
338         frequencyMap.put(new NonMovingAlloc(), 0.075);        //  15/200
339 
340         return frequencyMap;
341     }
342 
createLockFrequencyMap(Object lock)343     private final static Map<Operation, Double> createLockFrequencyMap(Object lock) {
344       Map<Operation, Double> frequencyMap = new HashMap<Operation, Double>();
345       frequencyMap.put(new Sleep(), 0.2);                     //  40/200
346       frequencyMap.put(new TimedWait(lock), 0.2);             //  40/200
347       frequencyMap.put(new Wait(lock), 0.2);                  //  40/200
348       frequencyMap.put(new SyncAndWork(lock), 0.4);           //  80/200
349 
350       return frequencyMap;
351     }
352 
main(String[] args)353     public static void main(String[] args) throws Exception {
354         System.loadLibrary(args[0]);
355         parseAndRun(args);
356     }
357 
updateFrequencyMap(Map<Operation, Double> in, Object lock, Semaphore semaphore, String arg)358     private static Map<Operation, Double> updateFrequencyMap(Map<Operation, Double> in,
359             Object lock, Semaphore semaphore, String arg) {
360         String split[] = arg.split(":");
361         if (split.length != 2) {
362             throw new IllegalArgumentException("Can't split argument " + arg);
363         }
364         double d;
365         try {
366             d = Double.parseDouble(split[1]);
367         } catch (Exception e) {
368             throw new IllegalArgumentException(e);
369         }
370         if (d < 0) {
371             throw new IllegalArgumentException(arg + ": value must be >= 0.");
372         }
373         Operation op = null;
374         if (split[0].equals("-oom")) {
375             op = new OOM();
376         } else if (split[0].equals("-sigquit")) {
377             op = new SigQuit();
378         } else if (split[0].equals("-alloc")) {
379             op = new Alloc();
380         } else if (split[0].equals("-largealloc")) {
381             op = new LargeAlloc();
382         } else if (split[0].equals("-stacktrace")) {
383             op = new StackTrace();
384         } else if (split[0].equals("-exit")) {
385             op = new Exit();
386         } else if (split[0].equals("-sleep")) {
387             op = new Sleep();
388         } else if (split[0].equals("-wait")) {
389             op = new Wait(lock);
390         } else if (split[0].equals("-timedwait")) {
391             op = new TimedWait(lock);
392         } else if (split[0].equals("-syncandwork")) {
393             op = new SyncAndWork(lock);
394         } else if (split[0].equals("-queuedwait")) {
395             op = new QueuedWait(semaphore);
396         } else {
397             throw new IllegalArgumentException("Unknown arg " + arg);
398         }
399 
400         if (in == null) {
401             in = new HashMap<Operation, Double>();
402         }
403         in.put(op, d);
404 
405         return in;
406     }
407 
normalize(Map<Operation, Double> map)408     private static void normalize(Map<Operation, Double> map) {
409         double sum = 0;
410         for (Double d : map.values()) {
411             sum += d;
412         }
413         if (sum == 0) {
414             throw new RuntimeException("No elements!");
415         }
416         if (sum != 1.0) {
417             // Avoid ConcurrentModificationException.
418             Set<Operation> tmp = new HashSet<>(map.keySet());
419             for (Operation op : tmp) {
420                 map.put(op, map.get(op) / sum);
421             }
422         }
423     }
424 
parseAndRun(String[] args)425     public static void parseAndRun(String[] args) throws Exception {
426         int numberOfThreads = -1;
427         int numberOfDaemons = -1;
428         int totalOperations = -1;
429         int operationsPerThread = -1;
430         int permits = -1;
431         Object lock = new Object();
432         Map<Operation, Double> frequencyMap = null;
433         boolean dumpMap = false;
434 
435         if (args != null) {
436             // args[0] is libarttest
437             for (int i = 1; i < args.length; i++) {
438                 if (args[i].equals("-n")) {
439                     i++;
440                     numberOfThreads = Integer.parseInt(args[i]);
441                 } else if (args[i].equals("-d")) {
442                     i++;
443                     numberOfDaemons = Integer.parseInt(args[i]);
444                 } else if (args[i].equals("-o")) {
445                     i++;
446                     totalOperations = Integer.parseInt(args[i]);
447                 } else if (args[i].equals("-t")) {
448                     i++;
449                     operationsPerThread = Integer.parseInt(args[i]);
450                 } else if (args[i].equals("-p")) {
451                     i++;
452                     permits = Integer.parseInt(args[i]);
453                 } else if (args[i].equals("--locks-only")) {
454                     frequencyMap = createLockFrequencyMap(lock);
455                 } else if (args[i].equals("--allocs-only")) {
456                     frequencyMap = createAllocFrequencyMap();
457                 } else if (args[i].equals("--dumpmap")) {
458                     dumpMap = true;
459                 } else {
460                     // Processing an argument of the form "-<operation>:X"
461                     // (where X is a double value).
462                     Semaphore semaphore = getSemaphore(permits);
463                     frequencyMap = updateFrequencyMap(frequencyMap, lock, semaphore, args[i]);
464                 }
465             }
466         }
467 
468         if (totalOperations != -1 && operationsPerThread != -1) {
469             throw new IllegalArgumentException(
470                     "Specified both totalOperations and operationsPerThread");
471         }
472 
473         if (numberOfThreads == -1) {
474             numberOfThreads = 5;
475         }
476 
477         if (numberOfDaemons == -1) {
478             numberOfDaemons = 3;
479         }
480 
481         if (totalOperations == -1) {
482             totalOperations = 1000;
483         }
484 
485         if (operationsPerThread == -1) {
486             operationsPerThread = totalOperations/numberOfThreads;
487         }
488 
489         if (frequencyMap == null) {
490             Semaphore semaphore = getSemaphore(permits);
491             frequencyMap = createDefaultFrequencyMap(lock, semaphore);
492         }
493         normalize(frequencyMap);
494 
495         if (dumpMap) {
496             System.out.println(frequencyMap);
497         }
498 
499         try {
500             runTest(numberOfThreads, numberOfDaemons, operationsPerThread, lock, frequencyMap);
501         } catch (Throwable t) {
502             // In this case, the output should not contain all the required
503             // "Finishing worker" lines.
504             Main.printThrowable(t);
505         }
506     }
507 
getSemaphore(int permits)508     private static Semaphore getSemaphore(int permits) {
509         if (permits == -1) {
510             // Default number of permits.
511             permits = 3;
512         }
513 
514         Semaphore semaphore = new Semaphore(permits, /* fair */ true);
515         forceTransitiveClassInitialization(semaphore, permits);
516         return semaphore;
517     }
518 
519     // Force ahead-of-time initialization of classes used by Semaphore
520     // code. Try to exercise all code paths likely to be taken during
521     // the actual test later (including having a thread blocking on
522     // the semaphore trying to acquire a permit), so that we increase
523     // the chances to initialize all classes indirectly used by
524     // QueuedWait (e.g. AbstractQueuedSynchronizer$Node).
forceTransitiveClassInitialization(Semaphore semaphore, final int permits)525     private static void forceTransitiveClassInitialization(Semaphore semaphore, final int permits) {
526         // Ensure `semaphore` has the expected number of permits
527         // before we start.
528         assert semaphore.availablePermits() == permits;
529 
530         // Let the main (current) thread acquire all permits from
531         // `semaphore`. Then create an auxiliary thread acquiring a
532         // permit from `semaphore`, blocking because none is
533         // available. Have the main thread release one permit, thus
534         // unblocking the second thread.
535 
536         // Auxiliary thread.
537         Thread auxThread = new Thread("Aux") {
538             public void run() {
539                 try {
540                     // Try to acquire one permit, and block until
541                     // that permit is released by the main thread.
542                     semaphore.acquire();
543                     // When unblocked, release the acquired permit
544                     // immediately.
545                     semaphore.release();
546                 } catch (InterruptedException ignored) {
547                     throw new RuntimeException("Test set up failed in auxiliary thread");
548                 }
549             }
550         };
551 
552         // Main thread.
553         try {
554             // Acquire all permits.
555             semaphore.acquire(permits);
556             // Start the auxiliary thread and have it try to acquire a
557             // permit.
558             auxThread.start();
559             // Synchronization: Wait until the auxiliary thread is
560             // blocked trying to acquire a permit from `semaphore`.
561             while (!semaphore.hasQueuedThreads()) {
562                 Thread.sleep(100);
563             }
564             // Release one permit, thus unblocking `auxThread` and let
565             // it acquire a permit.
566             semaphore.release();
567             // Synchronization: Wait for the auxiliary thread to die.
568             auxThread.join();
569             // Release remaining permits.
570             semaphore.release(permits - 1);
571 
572             // Verify that all permits have been released.
573             assert semaphore.availablePermits() == permits;
574         } catch (InterruptedException ignored) {
575             throw new RuntimeException("Test set up failed in main thread");
576         }
577     }
578 
runTest(final int numberOfThreads, final int numberOfDaemons, final int operationsPerThread, final Object lock, Map<Operation, Double> frequencyMap)579     public static void runTest(final int numberOfThreads, final int numberOfDaemons,
580                                final int operationsPerThread, final Object lock,
581                                Map<Operation, Double> frequencyMap) throws Exception {
582         final Thread mainThread = Thread.currentThread();
583         final Barrier startBarrier = new Barrier(numberOfThreads + numberOfDaemons + 1);
584 
585         // Each normal thread is going to do operationsPerThread
586         // operations. Each daemon thread will loop over all
587         // the operations and will not stop.
588         // The distribution of operations is determined by
589         // the frequencyMap values. We fill out an Operation[]
590         // for each thread with the operations it is to perform. The
591         // Operation[] is shuffled so that there is more random
592         // interactions between the threads.
593 
594         // Fill in the Operation[] array for each thread by laying
595         // down references to operation according to their desired
596         // frequency.
597         // The first numberOfThreads elements are normal threads, the last
598         // numberOfDaemons elements are daemon threads.
599         final Main[] threadStresses = new Main[numberOfThreads + numberOfDaemons];
600         for (int t = 0; t < threadStresses.length; t++) {
601             Operation[] operations = new Operation[operationsPerThread];
602             int o = 0;
603             LOOP:
604             while (true) {
605                 for (Operation op : frequencyMap.keySet()) {
606                     int freq = (int)(frequencyMap.get(op) * operationsPerThread);
607                     for (int f = 0; f < freq; f++) {
608                         if (o == operations.length) {
609                             break LOOP;
610                         }
611                         operations[o] = op;
612                         o++;
613                     }
614                 }
615             }
616             // Randomize the operation order
617             Collections.shuffle(Arrays.asList(operations));
618             threadStresses[t] = (t < numberOfThreads)
619                     ? new Main(lock, t, operations)
620                     : new Daemon(lock, t, operations, mainThread, startBarrier);
621         }
622 
623         // Enable to dump operation counts per thread to make sure its
624         // sane compared to frequencyMap.
625         if (DEBUG) {
626             for (int t = 0; t < threadStresses.length; t++) {
627                 Operation[] operations = threadStresses[t].operations;
628                 Map<Operation, Integer> distribution = new HashMap<Operation, Integer>();
629                 for (Operation operation : operations) {
630                     Integer ops = distribution.get(operation);
631                     if (ops == null) {
632                         ops = 1;
633                     } else {
634                         ops++;
635                     }
636                     distribution.put(operation, ops);
637                 }
638                 System.out.println("Distribution for " + t);
639                 for (Operation op : frequencyMap.keySet()) {
640                     System.out.println(op + " = " + distribution.get(op));
641                 }
642             }
643         }
644 
645         // Create the runners for each thread. The runner Thread
646         // ensures that thread that exit due to operation Exit will be
647         // restarted until they reach their desired
648         // operationsPerThread.
649         Thread[] runners = new Thread[numberOfThreads];
650         for (int r = 0; r < runners.length; r++) {
651             final Main ts = threadStresses[r];
652             runners[r] = new Thread("Runner thread " + r) {
653                 final Main threadStress = ts;
654                 public void run() {
655                     try {
656                         int id = threadStress.id;
657                         // No memory hungry task are running yet, so println() should succeed.
658                         System.out.println("Starting worker for " + id);
659                         // Wait until all runners and daemons reach the starting point.
660                         startBarrier.await();
661                         // Run the stress tasks.
662                         while (threadStress.nextOperation < operationsPerThread) {
663                             try {
664                                 Thread thread = new Thread(ts, "Worker thread " + id);
665                                 thread.start();
666                                 thread.join();
667 
668                                 if (DEBUG) {
669                                     System.out.println(
670                                         "Thread exited for " + id + " with " +
671                                         (operationsPerThread - threadStress.nextOperation) +
672                                         " operations remaining.");
673                                 }
674                             } catch (OutOfMemoryError e) {
675                                 // Ignore OOME since we need to print "Finishing worker"
676                                 // for the test to pass. This OOM can come from creating
677                                 // the Thread or from the DEBUG output.
678                                 // Note that the Thread creation may fail repeatedly,
679                                 // preventing the runner from making any progress,
680                                 // especially if the number of daemons is too high.
681                             }
682                         }
683                         // Print "Finishing worker" through JNI to avoid OOME.
684                         Main.printString(Main.finishingWorkerMessage);
685                     } catch (Throwable t) {
686                         Main.printThrowable(t);
687                         // Interrupt the main thread, so that it can orderly shut down
688                         // instead of waiting indefinitely for some Barrier.
689                         mainThread.interrupt();
690                     }
691                 }
692             };
693         }
694 
695         // The notifier thread is a daemon just loops forever to wake
696         // up threads in operation Wait.
697         if (lock != null) {
698             Thread notifier = new Thread("Notifier") {
699                 public void run() {
700                     while (true) {
701                         synchronized (lock) {
702                             lock.notifyAll();
703                         }
704                     }
705                 }
706             };
707             notifier.setDaemon(true);
708             notifier.start();
709         }
710 
711         // Create and start the daemon threads.
712         for (int r = 0; r < numberOfDaemons; r++) {
713             Main daemon = threadStresses[numberOfThreads + r];
714             Thread t = new Thread(daemon, "Daemon thread " + daemon.id);
715             t.setDaemon(true);
716             t.start();
717         }
718 
719         for (int r = 0; r < runners.length; r++) {
720             runners[r].start();
721         }
722         // Wait for all threads to reach the starting point.
723         startBarrier.await();
724         // Wait for runners to finish.
725         for (int r = 0; r < runners.length; r++) {
726             runners[r].join();
727         }
728     }
729 
730     protected final Operation[] operations;
731     private final Object lock;
732     protected final int id;
733 
734     private int nextOperation;
735 
Main(Object lock, int id, Operation[] operations)736     private Main(Object lock, int id, Operation[] operations) {
737         this.lock = lock;
738         this.id = id;
739         this.operations = operations;
740     }
741 
run()742     public void run() {
743         try {
744             if (DEBUG) {
745                 System.out.println("Starting ThreadStress " + id);
746             }
747             while (nextOperation < operations.length) {
748                 Operation operation = operations[nextOperation];
749                 if (DEBUG) {
750                     System.out.println("ThreadStress " + id
751                                        + " operation " + nextOperation
752                                        + " is " + operation);
753                 }
754                 nextOperation++;
755                 if (!operation.perform()) {
756                     return;
757                 }
758             }
759         } finally {
760             if (DEBUG) {
761                 System.out.println("Finishing ThreadStress for " + id);
762             }
763         }
764     }
765 
766     private static class Daemon extends Main {
Daemon(Object lock, int id, Operation[] operations, Thread mainThread, Barrier startBarrier)767         private Daemon(Object lock,
768                        int id,
769                        Operation[] operations,
770                        Thread mainThread,
771                        Barrier startBarrier) {
772             super(lock, id, operations);
773             this.mainThread = mainThread;
774             this.startBarrier = startBarrier;
775         }
776 
run()777         public void run() {
778             try {
779                 if (DEBUG) {
780                     System.out.println("Starting ThreadStress Daemon " + id);
781                 }
782                 startBarrier.await();
783                 try {
784                     int i = 0;
785                     while (true) {
786                         Operation operation = operations[i];
787                         if (DEBUG) {
788                             System.out.println("ThreadStress Daemon " + id
789                                                + " operation " + i
790                                                + " is " + operation);
791                         }
792                         // Ignore the result of the performed operation, making
793                         // Exit.perform() essentially a no-op for daemon threads.
794                         operation.perform();
795                         i = (i + 1) % operations.length;
796                     }
797                 } catch (OutOfMemoryError e) {
798                     // Catch OutOfMemoryErrors since these can cause the test to fail it they print
799                     // the stack trace after "Finishing worker". Note that operations should catch
800                     // their own OOME, this guards only agains OOME in the DEBUG output.
801                 }
802                 if (DEBUG) {
803                     System.out.println("Finishing ThreadStress Daemon for " + id);
804                 }
805             } catch (Throwable t) {
806                 Main.printThrowable(t);
807                 // Interrupt the main thread, so that it can orderly shut down
808                 // instead of waiting indefinitely for some Barrier.
809                 mainThread.interrupt();
810             }
811         }
812 
813         final Thread mainThread;
814         final Barrier startBarrier;
815     }
816 
817     // Note: java.util.concurrent.CyclicBarrier.await() allocates memory and may throw OOM.
818     // That is highly undesirable in this test, so we use our own simple barrier class.
819     // The only memory allocation that can happen here is the lock inflation which uses
820     // a native allocation. As such, it should succeed even if the Java heap is full.
821     // If the native allocation surprisingly fails, the program shall abort().
822     private static class Barrier {
Barrier(int initialCount)823         public Barrier(int initialCount) {
824             count = initialCount;
825         }
826 
await()827         public synchronized void await() throws InterruptedException {
828             --count;
829             if (count != 0) {
830                 do {
831                     wait();
832                 } while (count != 0);  // Check for spurious wakeup.
833             } else {
834                 notifyAll();
835             }
836         }
837 
838         private int count;
839     }
840 
841     // Printing a String/Throwable through JNI requires only native memory and space
842     // in the local reference table, so it should succeed even if the Java heap is full.
printString(String s)843     private static native void printString(String s);
printThrowable(Throwable t)844     private static native void printThrowable(Throwable t);
845 
846     static final String finishingWorkerMessage;
847     static final String errnoExceptionName;
848     static {
849         // We pre-allocate the strings in class initializer to avoid const-string
850         // instructions in code using these strings later as they may throw OOME.
851         finishingWorkerMessage = "Finishing worker\n";
852         errnoExceptionName = "ErrnoException";
853     }
854 }
855