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