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 }