1 /*
   2  * Copyright (c) 2013, 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 import java.util.concurrent.Phaser;
  25 import java.util.concurrent.atomic.AtomicInteger;
  26 import java.util.concurrent.locks.LockSupport;
  27 
  28 /**
  29  * ThreadStateController allows the caller to tranition to a specific 
  30  * thread state.
  31  */
  32 public class ThreadStateController extends Thread {
  33     // used to achieve waiting states
  34     private final Object lock;
  35     public ThreadStateController(String name, Object lock) {
  36         super(name);
  37         this.lock = lock;
  38     }
  39 
  40     public static void checkThreadState(ThreadStateController t, Thread.State expected) {
  41         // maximum number of retries when checking for thread state.
  42         final int MAX_RETRY = 500;
  43 
  44         // wait for the thread to transition to the expected state.
  45         // There is a small window between the thread checking the state
  46         // and the thread actual entering that state.
  47         Thread.State state;
  48         int retryCount=0;
  49         while ((state = t.getState()) != expected && retryCount < MAX_RETRY) {
  50             goSleep(10);
  51             retryCount++;
  52         }
  53 
  54         if (state == null) {
  55             throw new RuntimeException(t.getName() + " expected to have " +
  56                 expected + " but got null.");
  57         }
  58 
  59         if (state != expected) {
  60             throw new RuntimeException(String.format("%s expected in %s state but got %s " +
  61                 "(iterations %d interrupted %d)%n",
  62                 t.getName(), expected, state, t.iterations, t.interrupted));
  63         }
  64     }
  65 
  66     private static void goSleep(long ms) {
  67         try {
  68             Thread.sleep(ms);
  69         } catch (InterruptedException e) {
  70             throw new RuntimeException(e);
  71         }
  72     }
  73 
  74     // Phaser to sync between the main thread putting
  75     // this thread into various states
  76     private Phaser phaser =  new Phaser(2);
  77     private final int RUNNABLE = 0;
  78     private final int BLOCKED = 1;
  79     private final int WAITING = 2;
  80     private final int TIMED_WAITING = 3;
  81     private final int PARKED = 4;
  82     private final int TIMED_PARKED = 5;
  83     private final int SLEEPING = 6;
  84     private final int TERMINATE = 7;
  85 
  86     private volatile int state = RUNNABLE;
  87     private boolean done = false;
  88 
  89     // for debugging
  90     private AtomicInteger iterations = new AtomicInteger();
  91     private AtomicInteger interrupted = new AtomicInteger();
  92     public void run() {
  93         // Signal main thread to continue.
  94         phaser.arriveAndAwaitAdvance();
  95 
  96         while (!done) {
  97             iterations.incrementAndGet();
  98             switch (state) {
  99                 case RUNNABLE: {
 100                     double sum = 0;
 101                     for (int i = 0; i < 1000; i++) {
 102                        double r = Math.random();
 103                        double x = Math.pow(3, r);
 104                        sum += x - r;
 105                     }
 106                     break;
 107                 }
 108                 case BLOCKED: {
 109                     // signal main thread.
 110                     phaser.arrive();
 111                     System.out.println("  myThread is going to block.");
 112                     synchronized (lock) {
 113                         // finish blocking
 114                         state = RUNNABLE;
 115                     }
 116                     break;
 117                 }
 118                 case WAITING: {
 119                     synchronized (lock) {
 120                         // signal main thread.
 121                         phaser.arrive();
 122                         System.out.println("  myThread is going to wait.");
 123                         try {
 124                             lock.wait();
 125                         } catch (InterruptedException e) {
 126                             // ignore
 127                             interrupted.incrementAndGet();
 128                         }
 129                     }
 130                     break;
 131                 }
 132                 case TIMED_WAITING: {
 133                     synchronized (lock) {
 134                         // signal main thread.
 135                         phaser.arrive();
 136                         System.out.println("  myThread is going to timed wait.");
 137                         try {
 138                             lock.wait(10000);
 139                         } catch (InterruptedException e) {
 140                             // ignore
 141                             interrupted.incrementAndGet();
 142                         }
 143                     }
 144                     break;
 145                 }
 146                 case PARKED: {
 147                     // signal main thread.
 148                     phaser.arrive();
 149                     System.out.println("  myThread is going to park.");
 150                     LockSupport.park();
 151                     // give a chance for the main thread to block
 152                     goSleep(10);
 153                     break;
 154                 }
 155                 case TIMED_PARKED: {
 156                     // signal main thread.
 157                     phaser.arrive();
 158                     System.out.println("  myThread is going to timed park.");
 159                     long deadline = System.currentTimeMillis() + 10000*1000;
 160                     LockSupport.parkUntil(deadline);
 161 
 162                     // give a chance for the main thread to block
 163                     goSleep(10);
 164                     break;
 165                 }
 166                 case SLEEPING: {
 167                     // signal main thread.
 168                     phaser.arrive();
 169                     System.out.println("  myThread is going to sleep.");
 170                     try {
 171                         Thread.sleep(1000000);
 172                     } catch (InterruptedException e) {
 173                         // finish sleeping
 174                     }
 175                     break;
 176                 }
 177                 case TERMINATE: {
 178                     done = true;
 179                     // signal main thread.
 180                     phaser.arrive();
 181                     break;
 182                 }
 183                 default:
 184                     break;
 185             }
 186         }
 187     }
 188 
 189     public void waitUntilStarted() {
 190         // wait for MyThread to start.
 191         phaser.arriveAndAwaitAdvance();
 192     }
 193 
 194     public void goBlocked() {
 195         System.out.println("Waiting myThread to go blocked.");
 196         setState(BLOCKED);
 197         // wait for MyThread to get to a point just before being blocked
 198         phaser.arriveAndAwaitAdvance();
 199     }
 200 
 201     public void goWaiting() {
 202         System.out.println("Waiting myThread to go waiting.");
 203         setState(WAITING);
 204         // wait for MyThread to get to just before wait on object.
 205         phaser.arriveAndAwaitAdvance();
 206     }
 207 
 208     public void goTimedWaiting() {
 209         System.out.println("Waiting myThread to go timed waiting.");
 210         setState(TIMED_WAITING);
 211         // wait for MyThread to get to just before timed wait call.
 212         phaser.arriveAndAwaitAdvance();
 213     }
 214 
 215     public void goParked() {
 216         System.out.println("Waiting myThread to go parked.");
 217         setState(PARKED);
 218         // wait for MyThread to get to just before parked.
 219         phaser.arriveAndAwaitAdvance();
 220     }
 221 
 222     public void goTimedParked() {
 223         System.out.println("Waiting myThread to go timed parked.");
 224         setState(TIMED_PARKED);
 225         // wait for MyThread to get to just before timed park.
 226         phaser.arriveAndAwaitAdvance();
 227     }
 228 
 229     public void goSleeping() {
 230         System.out.println("Waiting myThread to go sleeping.");
 231         setState(SLEEPING);
 232         // wait for MyThread to get to just before sleeping
 233         phaser.arriveAndAwaitAdvance();
 234     }
 235 
 236     public void terminate() {
 237         System.out.println("Waiting myThread to terminate.");
 238         setState(TERMINATE);
 239         // wait for MyThread to get to just before terminate
 240         phaser.arriveAndAwaitAdvance();
 241     }
 242 
 243     private void setState(int newState) {
 244         switch (state) {
 245             case BLOCKED:
 246                 while (state == BLOCKED) {
 247                     goSleep(10);
 248                 }
 249                 state = newState;
 250                 break;
 251             case WAITING:
 252             case TIMED_WAITING:
 253                 state = newState;
 254                 synchronized (lock) {
 255                     lock.notify();
 256                 }
 257                 break;
 258             case PARKED:
 259             case TIMED_PARKED:
 260                 state = newState;
 261                 LockSupport.unpark(this);
 262                 break;
 263             case SLEEPING:
 264                 state = newState;
 265                 this.interrupt();
 266                 break;
 267             default:
 268                 state = newState;
 269                 break;
 270         }
 271         iterations.set(0);
 272         interrupted.set(0);
 273     }
 274 }