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