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.List;
19 import java.util.concurrent.atomic.AtomicInteger;
20 import java.util.concurrent.CyclicBarrier;
21 
22 public class Main implements Runnable {
23 
24     // Timeout in minutes. Make it larger than the run-test timeout to get a native thread dump by
25     // ART on timeout when running on the host.
26     private final static long TIMEOUT_VALUE = 7;
27 
28     private final static long MAX_SIZE = 1000;  // Maximum size of array-list to allocate.
29 
30     private final static int THREAD_COUNT = 16;
31 
32     // Use a couple of different forms of synchronizing to test some of these...
33     private final static AtomicInteger counter = new AtomicInteger();
34     private final static Object gate = new Object();
35     private volatile static int waitCount = 0;
36 
main(String[] args)37     public static void main(String[] args) throws Exception {
38         Thread[] threads = new Thread[THREAD_COUNT];
39 
40         // This barrier is used to synchronize the threads starting to allocate.
41         // Note: Even though a barrier is not allocation-free, this one is fine, as it will be used
42         //       before filling the heap.
43         CyclicBarrier startBarrier = new CyclicBarrier(threads.length);
44 
45         for (int i = 0; i < threads.length; i++) {
46             threads[i] = new Thread(new Main(startBarrier));
47             threads[i].start();
48         }
49 
50         // Wait for the threads to finish.
51         for (Thread thread : threads) {
52             thread.join();
53         }
54 
55         // Allocate objects to definitely run GC before quitting.
56         allocateObjectsToRunGc();
57 
58         new ArrayList<Object>(50);
59     }
60 
allocateObjectsToRunGc()61     private static void allocateObjectsToRunGc() {
62       ArrayList<Object> l = new ArrayList<Object>();
63       try {
64           for (int i = 0; i < 100000; i++) {
65               l.add(new ArrayList<Object>(i));
66           }
67       } catch (OutOfMemoryError oom) {
68       }
69     }
70 
Main(CyclicBarrier startBarrier)71     private Main(CyclicBarrier startBarrier) {
72         this.startBarrier = startBarrier;
73     }
74 
75     private ArrayList<Object> store;
76     private CyclicBarrier startBarrier;
77 
run()78     public void run() {
79         try {
80             work();
81         } catch (Throwable t) {
82             // Any exception or error getting here is bad.
83             try {
84                 // May need allocations...
85                 t.printStackTrace(System.out);
86             } catch (Throwable tInner) {
87             }
88             System.exit(1);
89         }
90     }
91 
work()92     private void work() throws Exception {
93         // Any exceptions except an OOME in the allocation loop are bad and handed off to the
94         // caller which should abort the whole runtime.
95 
96         ArrayList<Object> l = new ArrayList<Object>();
97         store = l;  // Keep it alive.
98 
99         // Wait for the start signal.
100         startBarrier.await(TIMEOUT_VALUE, java.util.concurrent.TimeUnit.MINUTES);
101 
102         // Allocate.
103         try {
104             for (int i = 0; i < MAX_SIZE; i++) {
105                 l.add(new ArrayList<Object>(i));
106             }
107         } catch (OutOfMemoryError oome) {
108             // Fine, we're done.
109         }
110 
111         // Atomically increment the counter and check whether we were last.
112         int number = counter.incrementAndGet();
113 
114         if (number < THREAD_COUNT) {
115             // Not last.
116             synchronized (gate) {
117                 // Increment the wait counter.
118                 waitCount++;
119                 gate.wait(TIMEOUT_VALUE * 1000 * 60);
120             }
121         } else {
122             // Last. Wait until waitCount == THREAD_COUNT - 1.
123             for (int loops = 0; ; loops++) {
124                 synchronized (gate) {
125                     if (waitCount == THREAD_COUNT - 1) {
126                         // OK, everyone's waiting. Notify and break out.
127                         gate.notifyAll();
128                         break;
129                     } else if (loops > 40) {
130                         // 1s wait, too many tries.
131                         System.out.println("Waited too long for the last thread.");
132                         System.exit(1);
133                     }
134                 }
135                 // Wait a bit.
136                 Thread.sleep(25);
137             }
138         }
139 
140         store = null;  // Allow GC to reclaim it.
141     }
142 }
143