1 /* 2 * Copyright (c) 2007, 2018, Oracle 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 package nsk.share.runner; 24 25 import nsk.share.Wicket; 26 import nsk.share.gc.OOMStress; 27 import nsk.share.log.*; 28 import nsk.share.test.Stresser; 29 import nsk.share.test.ExecutionController; 30 import nsk.share.TestBug; 31 import java.util.List; 32 import java.util.ArrayList; 33 34 /** 35 * Helper to assist in running threads. 36 * 37 * This class starts a number of threads which run some tasks in cycle. 38 * They exit after some time or after some iterations as 39 * determined by RunParams. 40 */ 41 public class ThreadsRunner implements MultiRunner, LogAware, RunParamsAware { 42 43 private Log log; 44 private RunParams runParams; 45 private List<Runnable> runnables = new ArrayList<Runnable>(); 46 private List<ManagedThread> threads = new ArrayList<ManagedThread>(); 47 private Wicket wicket = new Wicket(); 48 private Wicket finished; 49 private boolean started = false; 50 private boolean successful = true; 51 52 public ThreadsRunner() { 53 this(RunParams.getInstance()); 54 } 55 56 public ThreadsRunner(RunParams runParams) { 57 setRunParams(runParams); 58 } 59 60 public final void setLog(Log log) { 61 this.log = log; 62 } 63 64 private class ManagedThread extends Thread { 65 66 private Stresser stresser; 67 private Throwable exception; 68 private Runnable test; 69 private boolean shouldWait; 70 71 public ManagedThread(Runnable test) { 72 super(test.toString()); 73 this.test = test; 74 this.shouldWait = true; 75 this.stresser = new Stresser(this.getName(), runParams.getStressOptions()); 76 } 77 78 public void run() { 79 wicket.waitFor(); 80 try { 81 stresser.start(runParams.getIterations()); 82 while (!this.isInterrupted() && stresser.iteration()) { 83 test.run(); 84 Thread.yield(); 85 } 86 waitForOtherThreads(); 87 } catch (OutOfMemoryError oom) { 88 waitForOtherThreads(); 89 if (test instanceof OOMStress) { 90 // Test stressing OOM, not a failure. 91 log.info("Caught OutOfMemoryError in OOM stress test, omitting exception."); 92 } else { 93 failWithException(oom); 94 } 95 } catch (Throwable t) { 96 waitForOtherThreads(); 97 failWithException(t); 98 } finally { 99 stresser.finish(); 100 } 101 } 102 103 private void waitForOtherThreads() { 104 if (shouldWait) { 105 shouldWait = false; 106 finished.unlock(); 107 finished.waitFor(); 108 } else { 109 throw new TestBug("Waiting a second time is not premitted"); 110 } 111 } 112 113 private void failWithException(Throwable t) { 114 log.debug("Exception in "); 115 log.debug(test); 116 log.debug(t); 117 exception = t; 118 } 119 120 public void forceFinish() { 121 stresser.forceFinish(); 122 if (runParams.isInterruptThreads()) { 123 log.debug("Interrupting: " + this); 124 this.interrupt(); 125 } 126 } 127 128 public final Throwable getException() { 129 return exception; 130 } 131 132 public final ExecutionController getExecutionController() { 133 return stresser; 134 } 135 } 136 137 public void add(Runnable runnable) { 138 runnables.add(runnable); 139 } 140 141 public void remove(Runnable runnable) { 142 runnables.remove(runnable); 143 } 144 145 public void removeAll() { 146 runnables.clear(); 147 } 148 149 private Runnable get(int index) { 150 return (Runnable) runnables.get(index); 151 } 152 153 public Thread getThread(int index) { 154 return threads.get(index); 155 } 156 157 private int getCount() { 158 return runnables.size(); 159 } 160 161 private void prepare() { 162 } 163 164 private void create() { 165 int threadCount = runnables.size(); 166 finished = new Wicket(threadCount); 167 for (int i = 0; i < threadCount; ++i) { 168 threads.add(new ManagedThread(get(i))); 169 } 170 } 171 172 /** 173 * Start threads that run the tasks. 174 */ 175 public void start() { 176 if (started) { 177 return; 178 } 179 create(); 180 prepare(); 181 for (int i = 0; i < threads.size(); ++i) { 182 Thread t = (Thread) threads.get(i); 183 log.debug("Starting " + t); 184 t.start(); 185 } 186 wicket.unlock(); 187 started = true; 188 } 189 190 /** 191 * Stop threads that run the tasks. 192 */ 193 public void forceFinish() { 194 log.info("Forcing threads to finish"); 195 for (int i = 0; i < threads.size(); i++) { 196 ManagedThread thread = threads.get(i); 197 thread.forceFinish(); 198 } 199 } 200 201 /** 202 * Join threads that run the tasks. 203 */ 204 public void join() throws InterruptedException { 205 for (int i = 0; i < threads.size(); ++i) { 206 Thread t = (Thread) threads.get(i); 207 //log.debug("Joining " + t); 208 t.join(); 209 } 210 } 211 212 private int dumpFailures() { 213 int n = 0; 214 for (int i = 0; i < threads.size(); i++) { 215 ManagedThread thread = threads.get(i); 216 Throwable exception = thread.getException(); 217 if (exception != null) { 218 if (n == 0) { 219 log.error("Failures summary:"); 220 } 221 ++n; 222 log.error(exception); 223 } 224 } 225 if (n == 0) { 226 log.info("No unexpected exceptions/errors are thrown"); 227 } 228 return n; 229 } 230 231 private ManagedThread findManagedThread(Thread t) { 232 for (int i = 0; i < threads.size(); i++) { 233 ManagedThread mt = threads.get(i); 234 if (mt == t) { 235 return mt; 236 } 237 } 238 return null; 239 } 240 241 /** 242 * Run threads as determined by RunParams. 243 * 244 * Start threads, run for some time or for some number of iterations, 245 * then join and report if there were any exceptions. 246 * 247 * This method may additionally run other threads (as determined by RunParams): 248 * - thread that does System.gc() in cycle, @see GCRunner 249 * - thread that prints memory information in cycle, @see MemDiag 250 * - thread that prints information about FinMemoryObject's in cycle, @see FinDiag 251 * - thread that prints information about AllMemoryObject's in cycle, @see AllDiag 252 * 253 * @return true if there were no exceptions, false otherwise 254 */ 255 public void run() { 256 if (runParams.isRunGCThread()) { 257 add(new GCRunner()); 258 } 259 if (runParams.isRunFinThread()) { 260 add(new FinRunner()); 261 } 262 if (runParams.isRunMemDiagThread()) { 263 add(new MemDiag()); 264 } 265 try { 266 start(); 267 join(); 268 successful = dumpFailures() == 0; 269 } catch (Throwable t) { 270 log.info("Unexpected exception during the run."); 271 log.info(t); 272 successful = false; 273 } 274 } 275 276 public boolean isSuccessful() { 277 return successful; 278 } 279 280 public ExecutionController getExecutionController() { 281 Thread ct = Thread.currentThread(); 282 ManagedThread t = findManagedThread(ct); 283 if (t != null) { 284 return t.getExecutionController(); 285 } else { 286 throw new TestBug("Unable to find managed thread for thread (this method should be called from one of managed threads): " + ct); 287 } 288 } 289 290 public void runForever() { 291 start(); 292 } 293 294 public final void setRunParams(RunParams runParams) { 295 this.runParams = runParams; 296 } 297 }