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.TimeUnit; 26 import java.util.concurrent.TimeoutException; 27 import java.util.concurrent.atomic.AtomicInteger; 28 import java.util.concurrent.locks.LockSupport; 29 30 /** 31 * ThreadStateController allows a thread to request this thread to transition 32 * to a specific thread state. The {@linkplain #transitionTo request} is 33 * a blocking call that the calling thread will wait until this thread is about 34 * going to the new state. Only one request of state transition at a time 35 * is supported (the Phaser expects only parties of 2 to arrive and advance 36 * to next phase). 37 */ 38 public class ThreadStateController extends Thread { 39 // used to achieve waiting states 40 private final Object lock; 41 public ThreadStateController(String name, Object lock) { 42 super(name); 43 this.lock = lock; 44 } 45 46 public void checkThreadState(Thread.State expected) { 47 // maximum number of retries when checking for thread state. 48 final int MAX_RETRY = 500; 49 50 // wait for the thread to transition to the expected state. 51 // There is a small window between the thread checking the state 52 // and the thread actual entering that state. 53 Thread.State state; 54 int retryCount=0; 55 while ((state = getState()) != expected && retryCount < MAX_RETRY) { 56 goSleep(10); 57 retryCount++; 58 } 59 60 if (state == null) { 61 throw new RuntimeException(getName() + " expected to have " + 62 expected + " but got null."); 63 } 64 65 if (state != expected) { 66 throw new RuntimeException(String.format("%s expected in %s state but got %s " + 67 "(iterations %d interrupted %d)%n", 68 getName(), expected, state, iterations.get(), interrupted.get())); 69 } 70 } 71 72 public void goSleep(long ms) { 73 try { 74 Thread.sleep(ms); 75 } catch (InterruptedException e) { 76 throw new RuntimeException(e); 77 } 78 } 79 80 // Phaser to sync between the main thread putting 81 // this thread into various states 82 private Phaser phaser = new Phaser(2); 83 private volatile int newState = 0; 84 private volatile int state = 0; 85 private boolean done = false; 86 87 private static final int RUNNABLE = 1; 88 private static final int BLOCKED = 2; 89 private static final int WAITING = 3; 90 private static final int TIMED_WAITING = 4; 91 private static final int PARKED = 5; 92 private static final int TIMED_PARKED = 6; 93 private static final int SLEEPING = 7; 94 private static final int TERMINATE = 8; 95 96 // for debugging 97 private AtomicInteger iterations = new AtomicInteger(); 98 private AtomicInteger interrupted = new AtomicInteger(); 99 public void run() { 100 // this thread has started 101 phaser.arriveAndAwaitAdvance(); 102 while (!done) { 103 // state transition 104 int nextState = state; 105 if (newState != state) { 106 nextState = newState; 107 iterations.set(0); 108 interrupted.set(0); 109 } 110 iterations.incrementAndGet(); 111 switch (nextState) { 112 case RUNNABLE: { 113 stateChange(nextState); 114 double sum = 0; 115 for (int i = 0; i < 1000; i++) { 116 double r = Math.random(); 117 double x = Math.pow(3, r); 118 sum += x - r; 119 } 120 break; 121 } 122 case BLOCKED: { 123 System.out.format("%s is going to block (interations %d)%n", 124 getName(), iterations.get()); 125 stateChange(nextState); 126 // going to block on lock 127 synchronized (lock) { 128 System.out.format("%s acquired the lock (interations %d)%n", 129 getName(), iterations.get()); 130 } 131 break; 132 } 133 case WAITING: { 134 synchronized (lock) { 135 System.out.format("%s is going to waiting (interations %d interrupted %d)%n", 136 getName(), iterations.get(), interrupted.get()); 137 try { 138 stateChange(nextState); 139 lock.wait(); 140 } catch (InterruptedException e) { 141 // ignore 142 interrupted.incrementAndGet(); 143 } 144 } 145 break; 146 } 147 case TIMED_WAITING: { 148 synchronized (lock) { 149 System.out.format("%s is going to waiting (interations %d interrupted %d)%n", 150 getName(), iterations.get(), interrupted.get()); 151 try { 152 stateChange(nextState); 153 lock.wait(10000); 154 } catch (InterruptedException e) { 155 // ignore 156 interrupted.incrementAndGet(); 157 } 158 } 159 break; 160 } 161 case PARKED: { 162 System.out.format("%s is going to park (interations %d)%n", 163 getName(), iterations.get()); 164 stateChange(nextState); 165 LockSupport.park(); 166 break; 167 } 168 case TIMED_PARKED: { 169 System.out.format("%s is going to timed park (interations %d)%n", 170 getName(), iterations.get()); 171 long deadline = System.currentTimeMillis() + 10000*1000; 172 stateChange(nextState); 173 LockSupport.parkUntil(deadline); 174 break; 175 } 176 case SLEEPING: { 177 System.out.format("%s is going to sleep (interations %d interrupted %d)%n", 178 getName(), iterations.get(), interrupted.get()); 179 try { 180 stateChange(nextState); 181 Thread.sleep(1000000); 182 } catch (InterruptedException e) { 183 // finish sleeping 184 interrupted.incrementAndGet(); 185 } 186 break; 187 } 188 case TERMINATE: { 189 done = true; 190 stateChange(nextState); 191 break; 192 } 193 default: 194 break; 195 } 196 } 197 } 198 199 /** 200 * Change the state if it matches newState. 201 */ 202 private void stateChange(int nextState) { 203 // no state change 204 if (state == nextState) 205 return; 206 207 // transition to the new state 208 if (newState == nextState) { 209 state = nextState; 210 phaser.arrive(); 211 return; 212 } 213 214 // should never reach here 215 throw new RuntimeException("current " + state + " next " + nextState + 216 " new state " + newState); 217 } 218 /** 219 * Blocks until this thread transitions to the given state 220 */ 221 public void transitionTo(Thread.State tstate) throws InterruptedException { 222 switch (tstate) { 223 case RUNNABLE: 224 nextState(RUNNABLE); 225 break; 226 case BLOCKED: 227 nextState(BLOCKED); 228 break; 229 case WAITING: 230 nextState(WAITING); 231 break; 232 case TIMED_WAITING: 233 nextState(TIMED_WAITING); 234 break; 235 case TERMINATED: 236 nextState(TERMINATE); 237 break; 238 default: 239 break; 240 } 241 } 242 243 /** 244 * Blocks until this thread transitions to sleeping 245 */ 246 public void transitionToSleep() throws InterruptedException { 247 nextState(SLEEPING); 248 } 249 250 /** 251 * Blocks until this thread transitions to park or timed park 252 */ 253 public void transitionToPark(boolean timed) throws InterruptedException { 254 nextState(timed ? TIMED_PARKED : PARKED); 255 } 256 257 private void nextState(int newState) throws InterruptedException { 258 System.out.println("wait until the thread transitions to " + toStateName(newState)); 259 this.newState = newState; 260 int phase = phaser.arrive(); 261 for (;;) { 262 // when this thread has changed its state before it waits or parks 263 // on a lock, a potential race might happen if it misses the notify 264 // or unpark. Hence awaitArrive on the phaser with timeout to handle 265 // this race condition. 266 switch (state) { 267 case WAITING: 268 case TIMED_WAITING: 269 synchronized (lock) { 270 lock.notify(); 271 } 272 break; 273 case PARKED: 274 case TIMED_PARKED: 275 LockSupport.unpark(this); 276 break; 277 case SLEEPING: 278 this.interrupt(); 279 break; 280 case BLOCKED: 281 default: 282 break; 283 } 284 try { 285 phaser.awaitAdvanceInterruptibly(phase, 100, TimeUnit.MILLISECONDS); 286 return; 287 } catch (TimeoutException ex) { 288 // this thread hasn't arrived at this phase 289 } 290 } 291 } 292 293 private String toStateName(int state) { 294 switch (state) { 295 case RUNNABLE: 296 return "runnable"; 297 case WAITING: 298 return "waiting"; 299 case TIMED_WAITING: 300 return "timed waiting"; 301 case PARKED: 302 return "parked"; 303 case TIMED_PARKED: 304 return "timed parked"; 305 case SLEEPING: 306 return "sleeping"; 307 case BLOCKED: 308 return "blocked"; 309 case TERMINATE: 310 return "terminate"; 311 default: 312 return "unknown " + state; 313 } 314 } 315 }