1 /*
   2  * Copyright (c) 2013, 2019, 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 
  24 package jdk.testlibrary.thread;
  25 
  26 import java.util.concurrent.TimeoutException;
  27 
  28 /**
  29  * Thread which catches exceptions thrown during the execution
  30  * and stores them for later analysis.
  31  *
  32  * <pre>
  33  * {@code
  34  * TestThread thread = new TestThread(new XRun() {
  35  *      public void run() {
  36  *      // do something
  37  *      }
  38  * });
  39  * thread.start();
  40  * // do something
  41  * Throwable uncaught = thread.getUncaught();
  42  * }
  43  * </pre>
  44  */
  45 public class TestThread extends Thread {
  46 
  47     private final Runnable runnable;
  48     private volatile Throwable uncaught;
  49 
  50     /**
  51      * Returns {@link Runnable} the thread has been created with.
  52      *
  53      * @return The object whose {@code run} method is called
  54      */
  55     public Runnable getRunnable() {
  56         return runnable;
  57     }
  58 
  59     /**
  60      * Creates a new {@code TestThread} object.
  61      *
  62      * @param target The object whose {@code run} method is called
  63      * @param name The thread name
  64      */
  65     public TestThread(Runnable target, String name) {
  66         super(target, name);
  67         this.runnable = target;
  68     }
  69 
  70     /**
  71      * Creates a new {@code TestThread} object.
  72      *
  73      * @param target The object whose {@code run} method is called
  74      */
  75     public TestThread(Runnable target) {
  76         super(target);
  77         this.runnable = target;
  78     }
  79 
  80     /**
  81      * Creates a new {@code TestThread} object.
  82      *
  83      * @param group The thread group
  84      * @param target The object whose {@code run} method is called
  85      * @param name The thread name
  86      * @param stackSize Stack size
  87      */
  88     public TestThread(ThreadGroup group, Runnable target, String name,
  89             long stackSize) {
  90         super(group, target, name, stackSize);
  91         this.runnable = target;
  92     }
  93 
  94     /**
  95      * Creates a new {@code TestThread} object.
  96      *
  97      * @param group The thread group
  98      * @param target The object whose {@code run} method is called
  99      * @param name The thread name
 100      */
 101     public TestThread(ThreadGroup group, Runnable target, String name) {
 102         super(group, target, name);
 103         this.runnable = target;
 104     }
 105 
 106     /**
 107      * Creates a new {@code TestThread} object.
 108      *
 109      * @param group The thread group
 110      * @param target The object whose {@code run} method is called
 111      */
 112     public TestThread(ThreadGroup group, Runnable target) {
 113         super(group, target);
 114         this.runnable = target;
 115     }
 116 
 117     /**
 118      * The thread executor.
 119      */
 120     @Override
 121     public void run() {
 122         try {
 123             super.run();
 124         } catch (Throwable t) {
 125             uncaught = t;
 126         }
 127     }
 128 
 129     /**
 130      * Returns exception caught during the execution.
 131      *
 132      * @return {@link Throwable}
 133      */
 134     public Throwable getUncaught() {
 135         return uncaught;
 136     }
 137 
 138     /**
 139      * Waits for {@link TestThread} to die
 140      * and throws exception caught during the execution.
 141      *
 142      * @throws InterruptedException
 143      * @throws Throwable
 144      */
 145     public void joinAndThrow() throws InterruptedException, Throwable {
 146         join();
 147         if (uncaught != null) {
 148             throw uncaught;
 149         }
 150     }
 151 
 152     /**
 153      * Waits during {@code timeout} for {@link TestThread} to die
 154      * and throws exception caught during the execution.
 155      *
 156      * @param timeout The time to wait in milliseconds
 157      * @throws InterruptedException
 158      * @throws Throwable
 159      */
 160     public void joinAndThrow(long timeout) throws InterruptedException,
 161             Throwable {
 162         join(timeout);
 163         if (isAlive()) {
 164             throw new TimeoutException();
 165         }
 166         if (uncaught != null) {
 167             throw uncaught;
 168         }
 169     }
 170 
 171     /**
 172      * Waits for {@link TestThread} to die
 173      * and returns exception caught during the execution.
 174      *
 175      * @return Exception caught during the execution
 176      * @throws InterruptedException
 177      */
 178     public Throwable joinAndReturn() throws InterruptedException {
 179         join();
 180         if (uncaught != null) {
 181             return uncaught;
 182         }
 183         return null;
 184     }
 185 
 186     /**
 187      * Waits during {@code timeout} for {@link TestThread} to die
 188      * and returns exception caught during the execution.
 189      *
 190      * @param timeout The time to wait in milliseconds
 191      * @return Exception caught during the execution
 192      * @throws InterruptedException
 193      */
 194     public Throwable joinAndReturn(long timeout) throws InterruptedException {
 195         join(timeout);
 196         if (isAlive()) {
 197             return new TimeoutException();
 198         }
 199         if (uncaught != null) {
 200             return uncaught;
 201         }
 202         return null;
 203     }
 204 }
 205