| /* |
| * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| package nsk.share.runner; |
| |
| import nsk.share.Wicket; |
| import nsk.share.gc.OOMStress; |
| import nsk.share.log.*; |
| import nsk.share.test.Stresser; |
| import nsk.share.test.ExecutionController; |
| import nsk.share.TestBug; |
| import java.util.List; |
| import java.util.ArrayList; |
| |
| /** |
| * Helper to assist in running threads. |
| * |
| * This class starts a number of threads which run some tasks in cycle. |
| * They exit after some time or after some iterations as |
| * determined by RunParams. |
| */ |
| public class ThreadsRunner implements MultiRunner, LogAware, RunParamsAware { |
| |
| private Log log; |
| private RunParams runParams; |
| private List<Runnable> runnables = new ArrayList<Runnable>(); |
| private List<ManagedThread> threads = new ArrayList<ManagedThread>(); |
| private Wicket wicket = new Wicket(); |
| private Wicket finished; |
| private boolean started = false; |
| private boolean successful = true; |
| |
| public ThreadsRunner() { |
| this(RunParams.getInstance()); |
| } |
| |
| public ThreadsRunner(RunParams runParams) { |
| setRunParams(runParams); |
| } |
| |
| public final void setLog(Log log) { |
| this.log = log; |
| } |
| |
| private class ManagedThread extends Thread { |
| |
| private Stresser stresser; |
| private Throwable exception; |
| private Runnable test; |
| private boolean shouldWait; |
| |
| public ManagedThread(Runnable test) { |
| super(test.toString()); |
| this.test = test; |
| this.shouldWait = true; |
| this.stresser = new Stresser(this.getName(), runParams.getStressOptions()); |
| } |
| |
| public void run() { |
| wicket.waitFor(); |
| try { |
| stresser.start(runParams.getIterations()); |
| while (!this.isInterrupted() && stresser.iteration()) { |
| test.run(); |
| yield(); |
| } |
| waitForOtherThreads(); |
| } catch (OutOfMemoryError oom) { |
| waitForOtherThreads(); |
| if (test instanceof OOMStress) { |
| // Test stressing OOM, not a failure. |
| log.info("Caught OutOfMemoryError in OOM stress test, omitting exception."); |
| } else { |
| failWithException(oom); |
| } |
| } catch (Throwable t) { |
| waitForOtherThreads(); |
| failWithException(t); |
| } finally { |
| stresser.finish(); |
| } |
| } |
| |
| private void waitForOtherThreads() { |
| if (shouldWait) { |
| shouldWait = false; |
| finished.unlock(); |
| finished.waitFor(); |
| } else { |
| throw new TestBug("Waiting a second time is not premitted"); |
| } |
| } |
| |
| private void failWithException(Throwable t) { |
| log.debug("Exception in "); |
| log.debug(test); |
| log.debug(t); |
| exception = t; |
| } |
| |
| public void forceFinish() { |
| stresser.forceFinish(); |
| if (runParams.isInterruptThreads()) { |
| log.debug("Interrupting: " + this); |
| this.interrupt(); |
| } |
| } |
| |
| public final Throwable getException() { |
| return exception; |
| } |
| |
| public final ExecutionController getExecutionController() { |
| return stresser; |
| } |
| } |
| |
| public void add(Runnable runnable) { |
| runnables.add(runnable); |
| } |
| |
| public void remove(Runnable runnable) { |
| runnables.remove(runnable); |
| } |
| |
| public void removeAll() { |
| runnables.clear(); |
| } |
| |
| private Runnable get(int index) { |
| return (Runnable) runnables.get(index); |
| } |
| |
| public Thread getThread(int index) { |
| return threads.get(index); |
| } |
| |
| private int getCount() { |
| return runnables.size(); |
| } |
| |
| private void prepare() { |
| } |
| |
| private void create() { |
| int threadCount = runnables.size(); |
| finished = new Wicket(threadCount); |
| for (int i = 0; i < threadCount; ++i) { |
| threads.add(new ManagedThread(get(i))); |
| } |
| } |
| |
| /** |
| * Start threads that run the tasks. |
| */ |
| public void start() { |
| if (started) { |
| return; |
| } |
| create(); |
| prepare(); |
| for (int i = 0; i < threads.size(); ++i) { |
| Thread t = (Thread) threads.get(i); |
| log.debug("Starting " + t); |
| t.start(); |
| } |
| wicket.unlock(); |
| started = true; |
| } |
| |
| /** |
| * Stop threads that run the tasks. |
| */ |
| public void forceFinish() { |
| log.info("Forcing threads to finish"); |
| for (int i = 0; i < threads.size(); i++) { |
| ManagedThread thread = threads.get(i); |
| thread.forceFinish(); |
| } |
| } |
| |
| /** |
| * Join threads that run the tasks. |
| */ |
| public void join() throws InterruptedException { |
| for (int i = 0; i < threads.size(); ++i) { |
| Thread t = (Thread) threads.get(i); |
| //log.debug("Joining " + t); |
| t.join(); |
| } |
| } |
| |
| private int dumpFailures() { |
| int n = 0; |
| for (int i = 0; i < threads.size(); i++) { |
| ManagedThread thread = threads.get(i); |
| Throwable exception = thread.getException(); |
| if (exception != null) { |
| if (n == 0) { |
| log.error("Failures summary:"); |
| } |
| ++n; |
| log.error(exception); |
| } |
| } |
| if (n == 0) { |
| log.info("No unexpected exceptions/errors are thrown"); |
| } |
| return n; |
| } |
| |
| private ManagedThread findManagedThread(Thread t) { |
| for (int i = 0; i < threads.size(); i++) { |
| ManagedThread mt = threads.get(i); |
| if (mt == t) { |
| return mt; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Run threads as determined by RunParams. |
| * |
| * Start threads, run for some time or for some number of iterations, |
| * then join and report if there were any exceptions. |
| * |
| * This method may additionally run other threads (as determined by RunParams): |
| * - thread that does System.gc() in cycle, @see GCRunner |
| * - thread that prints memory information in cycle, @see MemDiag |
| * - thread that prints information about FinMemoryObject's in cycle, @see FinDiag |
| * - thread that prints information about AllMemoryObject's in cycle, @see AllDiag |
| * |
| * @return true if there were no exceptions, false otherwise |
| */ |
| public void run() { |
| if (runParams.isRunGCThread()) { |
| add(new GCRunner()); |
| } |
| if (runParams.isRunFinThread()) { |
| add(new FinRunner()); |
| } |
| if (runParams.isRunMemDiagThread()) { |
| add(new MemDiag()); |
| } |
| try { |
| start(); |
| join(); |
| successful = dumpFailures() == 0; |
| } catch (Throwable t) { |
| log.info("Unexpected exception during the run."); |
| log.info(t); |
| successful = false; |
| } |
| } |
| |
| public boolean isSuccessful() { |
| return successful; |
| } |
| |
| public ExecutionController getExecutionController() { |
| Thread ct = Thread.currentThread(); |
| ManagedThread t = findManagedThread(ct); |
| if (t != null) { |
| return t.getExecutionController(); |
| } else { |
| throw new TestBug("Unable to find managed thread for thread (this method should be called from one of managed threads): " + ct); |
| } |
| } |
| |
| public void runForever() { |
| start(); |
| } |
| |
| public final void setRunParams(RunParams runParams) { |
| this.runParams = runParams; |
| } |
| } |