1 /*
   2  * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 package MyPackage;
  25 
  26 import java.util.ArrayList;
  27 import java.util.List;
  28 import java.util.concurrent.BlockingQueue;
  29 import java.util.concurrent.LinkedBlockingQueue;
  30 
  31 /** API for handling heap allocating threads. */
  32 class ThreadInformation {
  33   private Thread thread;
  34   private Allocator allocator;
  35 
  36   public ThreadInformation(Thread thread, Allocator allocator) {
  37     this.thread = thread;
  38     this.allocator = allocator;
  39   }
  40 
  41   public void waitForJobDone() {
  42     allocator.waitForJobDone();
  43   }
  44 
  45   public void stop() {
  46     try {
  47       allocator.stopRun();
  48       thread.join();
  49 
  50       if (!allocator.endedNormally()) {
  51         throw new RuntimeException("Thread did not end normally...");
  52       }
  53 
  54     } catch(InterruptedException e) {
  55       throw new RuntimeException("Thread got interrupted...");
  56     }
  57   }
  58 
  59   private void start() {
  60     allocator.start();
  61   }
  62 
  63   public static void startThreads(List<ThreadInformation> threadList) {
  64     for (ThreadInformation info : threadList) {
  65       info.start();
  66     }
  67   }
  68 
  69   public static void stopThreads(List<ThreadInformation> threadList) {
  70     for (ThreadInformation info : threadList) {
  71       info.stop();
  72     }
  73   }
  74 
  75   public Thread getThread() {
  76     return thread;
  77   }
  78 
  79   public static void waitForThreads(List<ThreadInformation> threadList) {
  80     System.err.println("Waiting for threads to be done");
  81     // Wait until all threads have put an object in the queue.
  82     for (ThreadInformation info : threadList) {
  83       info.waitForJobDone();
  84     }
  85   }
  86 
  87   public static List<ThreadInformation> createThreadList(int numThreads) {
  88     List<ThreadInformation> threadList = new ArrayList<>();
  89     for (int i = 0 ; i < numThreads; i++) {
  90       Allocator allocator = new Allocator(i);
  91       Thread thread = new Thread(allocator, "Allocator" + i);
  92 
  93       ThreadInformation info = new ThreadInformation(thread, allocator);
  94       threadList.add(info);
  95       thread.start();
  96     }
  97     return threadList;
  98   }
  99 }
 100 
 101 class Allocator implements Runnable {
 102   private int depth;
 103   private List<int[]> currentList;
 104   private BlockingQueue<Object> jobCanStart;
 105   private BlockingQueue<Object> jobDone;
 106   private BlockingQueue<Object> jobCanStop;
 107   private boolean failed;
 108 
 109   public Allocator(int depth) {
 110     this.jobCanStart = new LinkedBlockingQueue<>();
 111     this.jobDone = new LinkedBlockingQueue<>();
 112     this.jobCanStop = new LinkedBlockingQueue<>();
 113     this.depth = depth;
 114   }
 115 
 116   public boolean endedNormally() {
 117     return !failed;
 118   }
 119 
 120   private void helper() {
 121     List<int[]> newList = new ArrayList<>();
 122     // Let us assume that the array is 40 bytes of memory, keep in
 123     // memory at least 1.7MB without counting the link-list itself, which adds to this.
 124     int iterations = (1 << 20) / 24;
 125     for (int i = 0; i < iterations; i++) {
 126       int newTmp[] = new int[5];
 127       // Force it to be kept.
 128       newList.add(newTmp);
 129     }
 130 
 131     // Replace old list with new list, which provokes two things:
 132     //  Old list will get GC'd at some point.
 133     //  New list forces that this thread has some allocations still sampled.
 134     currentList = newList;
 135   }
 136 
 137   private void recursiveWrapper(int depth) {
 138     if (depth > 0) {
 139       recursiveWrapper(depth - 1);
 140       return;
 141     }
 142     helper();
 143   }
 144 
 145   public void stopRun() throws InterruptedException {
 146     jobCanStop.put(new Object());
 147   }
 148 
 149   public void run() {
 150     String name = Thread.currentThread().getName();
 151     System.err.println("Going to run: " + name);
 152     // Wait till we are told to really start.
 153     waitForStart();
 154 
 155     System.err.println("Running: " + name);
 156     for (int j = 0; j < 100; j++) {
 157       recursiveWrapper(depth);
 158     }
 159 
 160     try {
 161       // Tell the main thread we are done.
 162       jobDone.put(new Object());
 163 
 164       System.err.println("Waiting for main: " + name);
 165       // Wait until the main thread says we can stop.
 166       jobCanStop.take();
 167       System.err.println("Waited for main: " + name);
 168     } catch (InterruptedException e) {
 169       failed = true;
 170     }
 171   }
 172 
 173   public void waitForJobDone() {
 174     try {
 175       jobDone.take();
 176     } catch(InterruptedException e) {
 177       throw new RuntimeException("Thread got interrupted...");
 178     }
 179   }
 180 
 181   public void waitForStart() {
 182     try {
 183       jobCanStart.take();
 184     } catch(InterruptedException e) {
 185       throw new RuntimeException("Thread got interrupted...");
 186     }
 187   }
 188 
 189   public void start() {
 190     try {
 191       jobCanStart.put(new Object());
 192     } catch(InterruptedException e) {
 193       throw new RuntimeException("Thread got interrupted...");
 194     }
 195   }
 196 }