blob: b0a8086128097387238906c8c83ba072f70288a0 [file] [log] [blame]
/*
* Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package MyPackage;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
/** API for handling heap allocating threads. */
class ThreadInformation {
private Thread thread;
private Allocator allocator;
public ThreadInformation(Thread thread, Allocator allocator) {
this.thread = thread;
this.allocator = allocator;
}
public void waitForJobDone() {
allocator.waitForJobDone();
}
public void stop() {
try {
allocator.stopRun();
thread.join();
if (!allocator.endedNormally()) {
throw new RuntimeException("Thread did not end normally...");
}
} catch(InterruptedException e) {
throw new RuntimeException("Thread got interrupted...");
}
}
private void start() {
allocator.start();
}
public static void startThreads(List<ThreadInformation> threadList) {
for (ThreadInformation info : threadList) {
info.start();
}
}
public static void stopThreads(List<ThreadInformation> threadList) {
for (ThreadInformation info : threadList) {
info.stop();
}
}
public Thread getThread() {
return thread;
}
public static void waitForThreads(List<ThreadInformation> threadList) {
System.err.println("Waiting for threads to be done");
// Wait until all threads have put an object in the queue.
for (ThreadInformation info : threadList) {
info.waitForJobDone();
}
}
public static List<ThreadInformation> createThreadList(int numThreads) {
List<ThreadInformation> threadList = new ArrayList<>();
for (int i = 0 ; i < numThreads; i++) {
Allocator allocator = new Allocator(i);
Thread thread = new Thread(allocator, "Allocator" + i);
ThreadInformation info = new ThreadInformation(thread, allocator);
threadList.add(info);
thread.start();
}
return threadList;
}
}
class Allocator implements Runnable {
private int depth;
private List<int[]> currentList;
private BlockingQueue<Object> jobCanStart;
private BlockingQueue<Object> jobDone;
private BlockingQueue<Object> jobCanStop;
private boolean failed;
public Allocator(int depth) {
this.jobCanStart = new LinkedBlockingQueue<>();
this.jobDone = new LinkedBlockingQueue<>();
this.jobCanStop = new LinkedBlockingQueue<>();
this.depth = depth;
}
public boolean endedNormally() {
return !failed;
}
private void helper() {
List<int[]> newList = new ArrayList<>();
// Let us assume that the array is 40 bytes of memory, keep in
// memory at least 1.7MB without counting the link-list itself, which adds to this.
int iterations = (1 << 20) / 24;
for (int i = 0; i < iterations; i++) {
int newTmp[] = new int[5];
// Force it to be kept.
newList.add(newTmp);
}
// Replace old list with new list, which provokes two things:
// Old list will get GC'd at some point.
// New list forces that this thread has some allocations still sampled.
currentList = newList;
}
private void recursiveWrapper(int depth) {
if (depth > 0) {
recursiveWrapper(depth - 1);
return;
}
helper();
}
public void stopRun() throws InterruptedException {
jobCanStop.put(new Object());
}
public void run() {
String name = Thread.currentThread().getName();
System.err.println("Going to run: " + name);
// Wait till we are told to really start.
waitForStart();
System.err.println("Running: " + name);
for (int j = 0; j < 100; j++) {
recursiveWrapper(depth);
}
try {
// Tell the main thread we are done.
jobDone.put(new Object());
System.err.println("Waiting for main: " + name);
// Wait until the main thread says we can stop.
jobCanStop.take();
System.err.println("Waited for main: " + name);
} catch (InterruptedException e) {
failed = true;
}
}
public void waitForJobDone() {
try {
jobDone.take();
} catch(InterruptedException e) {
throw new RuntimeException("Thread got interrupted...");
}
}
public void waitForStart() {
try {
jobCanStart.take();
} catch(InterruptedException e) {
throw new RuntimeException("Thread got interrupted...");
}
}
public void start() {
try {
jobCanStart.put(new Object());
} catch(InterruptedException e) {
throw new RuntimeException("Thread got interrupted...");
}
}
}