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 BlockingQueue<Object> waitingQueue;
  35   private Allocator allocator;
  36 
  37   public ThreadInformation(Thread thread, BlockingQueue<Object> waitingQueue,
  38       Allocator allocator) {
  39     this.thread = thread;
  40     this.waitingQueue = waitingQueue;
  41     this.allocator = allocator;
  42   }
  43 
  44   public void waitForToken() {
  45     allocator.waitForToken();
  46   }
  47 
  48   public void stop() {
  49     try {
  50       allocator.stopRun();
  51       thread.join();
  52 
  53       if (!allocator.endedNormally()) {
  54         throw new RuntimeException("Thread did not end normally...");
  55       }
  56 
  57     } catch(InterruptedException e) {
  58       throw new RuntimeException("Thread got interrupted...");
  59     }
  60   }
  61 
  62   private void start() {
  63     allocator.start();
  64   }
  65 
  66   public static void startThreads(List<ThreadInformation> threadList) {
  67     for (ThreadInformation info : threadList) {
  68       info.start();
  69     }
  70   }
  71 
  72   public static void stopThreads(List<ThreadInformation> threadList) {
  73     for (ThreadInformation info : threadList) {
  74       info.stop();
  75     }
  76   }
  77 
  78   public Thread getThread() {
  79     return thread;
  80   }
  81 
  82   public static void waitForThreads(List<ThreadInformation> threadList) {
  83     // Wait until all threads have put an object in the queue.
  84     for (ThreadInformation info : threadList) {
  85       info.waitForToken();
  86     }
  87   }
  88 
  89   public static List<ThreadInformation> createThreadList(int numThreads) {
  90     List<ThreadInformation> threadList = new ArrayList<>();
  91     for (int i = 0 ; i < numThreads; i++) {
  92       BlockingQueue<Object> queue = new LinkedBlockingQueue<>();
  93       Allocator allocator = new Allocator(i, queue);
  94       Thread thread = new Thread(allocator, "Allocator" + i);
  95 
  96       ThreadInformation info = new ThreadInformation(thread, queue, allocator);
  97       threadList.add(info);
  98       thread.start();
  99     }
 100     return threadList;
 101   }
 102 }
 103 
 104 class Allocator implements Runnable {
 105   private int depth;
 106   private List<int[]> currentList;
 107   private BlockingQueue<Object> waitingQueue;
 108   private BlockingQueue<Object> jobCanStop;
 109   private boolean failed;
 110 
 111   public Allocator(int depth, BlockingQueue<Object> waitingQueue) {
 112     jobCanStop = new LinkedBlockingQueue<>();
 113     this.waitingQueue = waitingQueue;
 114     this.depth = depth;
 115   }
 116 
 117   public boolean endedNormally() {
 118     return !failed;
 119   }
 120 
 121   private void helper() {
 122     List<int[]> newList = new ArrayList<>();
 123     // Let us assume that the array is 24 bytes of memory, by default we sample at 512k, keep in
 124     // memory at least 2MB without counting the link-list itself, which adds to this.
 125     int iterations = (1 << 21) / 24;
 126     for (int i = 0; i < iterations; i++) {
 127       int newTmp[] = new int[1];
 128       // Force it to be kept.
 129       newList.add(newTmp);
 130     }
 131 
 132     // Replace old list with new list, which provokes two things:
 133     //  Old list will get GC'd at some point.
 134     //  New list forces that this thread has some allocations still sampled.
 135     currentList = newList;
 136   }
 137 
 138   private void recursiveWrapper(int depth) {
 139     if (depth > 0) {
 140       recursiveWrapper(depth - 1);
 141     }
 142     helper();
 143   }
 144 
 145   public void stopRun() throws InterruptedException {
 146     jobCanStop.put(new Object());
 147   }
 148 
 149   public void run() {
 150     // Wait till we are told to really start.
 151     waitForToken();
 152 
 153     for (int j = 0; j < 50; j++) {
 154       recursiveWrapper(depth);
 155     }
 156     System.err.println("Here done allocating");
 157 
 158     try {
 159       // Tell the main thread we are done.
 160       waitingQueue.put(new Object());
 161       System.err.println("Waited for queue");
 162 
 163       // Wait until the main thread says we can stop.
 164       jobCanStop.take();
 165     } catch (InterruptedException e) {
 166       failed = true;
 167     }
 168     System.err.println("Done !!!!");
 169   }
 170 
 171   public void waitForToken() {
 172     try {
 173       waitingQueue.take();
 174     } catch(InterruptedException e) {
 175       throw new RuntimeException("Thread got interrupted...");
 176     }
 177   }
 178 
 179   public void start() {
 180     try {
 181       waitingQueue.put(new Object());
 182     } catch(InterruptedException e) {
 183       throw new RuntimeException("Thread got interrupted...");
 184     }
 185   }
 186 }