/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class Main implements Runnable {
public final static long TIMEOUT_VALUE = 5; // Timeout in minutes.
public final static long MAX_SIZE = 1000; // Maximum size of array-list to allocate.
public static void main(String[] args) throws Exception {
Thread[] threads = new Thread[16];
// Use a cyclic system of synchronous queues to pass a boolean token around.
//
// The combinations are:
//
// Worker receives: true false false true
// Worker has OOM: false false true true
// |
// v
// Value to pass: true false false false
// Exit out of loop: false true true true
// Wait on in queue: true false false true
//
// Finally, the workers are supposed to wait on the barrier to synchronize the GC run.
CyclicBarrier barrier = new CyclicBarrier(threads.length);
List<SynchronousQueue<Boolean>> queues = new ArrayList<SynchronousQueue<Boolean>>(
threads.length);
for (int i = 0; i < threads.length; i++) {
queues.add(new SynchronousQueue<Boolean>());
}
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(new Main(i, queues.get(i), queues.get((i + 1) % threads.length),
barrier));
}
for (Thread thread : threads) {
thread.start();
}
// Push off the cycle.
checkTimeout(queues.get(0).offer(Boolean.TRUE, TIMEOUT_VALUE, TimeUnit.MINUTES));
// Wait for the threads to finish.
for (Thread thread : threads) {
thread.join();
}
// Allocate objects to definitely run GC before quitting.
try {
for (int i = 0; i < 1000; i++) {
new ArrayList<Object>(i);
}
} catch (OutOfMemoryError oom) {
}
}
private static void checkTimeout(Object o) {
checkTimeout(o != null);
}
private static void checkTimeout(boolean b) {
if (!b) {
// Something went wrong.
System.out.println("Bad things happened, timeout.");
System.exit(1);
}
}
private final int id;
private final SynchronousQueue<Boolean> waitOn;
private final SynchronousQueue<Boolean> pushTo;
private final CyclicBarrier finalBarrier;
private Main(int id, SynchronousQueue<Boolean> waitOn, SynchronousQueue<Boolean> pushTo,
CyclicBarrier finalBarrier) {
this.id = id;
this.waitOn = waitOn;
this.pushTo = pushTo;
this.finalBarrier = finalBarrier;
}
public void run() {
try {
work();
} catch (Exception exc) {
// Any exception is bad.
exc.printStackTrace(System.err);
System.exit(1);
}
}
public void work() throws BrokenBarrierException, InterruptedException, TimeoutException {
ArrayList<Object> l = new ArrayList<Object>();
// Main loop.
for (int i = 0; ; i++) {
Boolean receivedB = waitOn.poll(TIMEOUT_VALUE, TimeUnit.MINUTES);
checkTimeout(receivedB);
boolean received = receivedB;
// This is the first stage, try to allocate up till MAX_SIZE.
boolean oom = i >= MAX_SIZE;
try {
l.add(new ArrayList<Object>(i));
} catch (OutOfMemoryError oome) {
oom = true;
}
if (!received || oom) {
// First stage, always push false.
checkTimeout(pushTo.offer(Boolean.FALSE, TIMEOUT_VALUE, TimeUnit.MINUTES));
// If we received true, wait for the false to come around.
if (received) {
checkTimeout(waitOn.poll(TIMEOUT_VALUE, TimeUnit.MINUTES));
}
// Break out of the loop.
break;
} else {
// Pass on true.
checkTimeout(pushTo.offer(Boolean.TRUE, TIMEOUT_VALUE, TimeUnit.MINUTES));
}
}
// We have reached the final point. Wait on the barrier, but at most a minute.
finalBarrier.await(TIMEOUT_VALUE, TimeUnit.MINUTES);
// Done.
}
}