Path: blob/master/test/jdk/java/lang/Thread/ThreadStateController.java
41149 views
/*1* Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223import java.util.concurrent.Phaser;24import java.util.concurrent.TimeUnit;25import java.util.concurrent.TimeoutException;26import java.util.concurrent.atomic.AtomicInteger;27import java.util.concurrent.locks.LockSupport;2829import jdk.test.lib.LockFreeLogger;30import jdk.test.lib.Utils;3132/**33* ThreadStateController allows a thread to request this thread to transition34* to a specific thread state. The {@linkplain #transitionTo request} is35* a blocking call that the calling thread will wait until this thread is about36* going to the new state. Only one request of state transition at a time37* is supported (the Phaser expects only parties of 2 to arrive and advance38* to next phase).39*/40public class ThreadStateController extends Thread {41// used to achieve waiting states42private final Object lock;43public ThreadStateController(String name, Object lock) {44super(name);45this.lock = lock;46}4748public void checkThreadState(Thread.State expected) {49// maximum number of retries when checking for thread state.50final int MAX_RETRY = 500;5152// wait for the thread to transition to the expected state.53// There is a small window between the thread checking the state54// and the thread actual entering that state.55Thread.State state;56int retryCount=0;57while ((state = getState()) != expected && retryCount < MAX_RETRY) {58pause(10);59retryCount++;60}6162if (state == null) {63throw new RuntimeException(getName() + " expected to have " +64expected + " but got null.");65}6667if (state != expected) {68throw new RuntimeException(String.format("%s expected in %s state but got %s " +69"(iterations %d interrupted %d)%n",70getName(), expected, state, iterations.get(), interrupted.get()));71}72}7374public static void pause(long ms) {75try {76Thread.sleep(Utils.adjustTimeout(ms));77} catch (InterruptedException e) {78throw new RuntimeException(e);79}80}8182// Phaser to sync between the main thread putting83// this thread into various states84private final Phaser phaser = new Phaser(2);85private volatile int newState = S_RUNNABLE;86private volatile int state = 0;87private boolean done = false;8889private static final int S_RUNNABLE = 1;90private static final int S_BLOCKED = 2;91private static final int S_WAITING = 3;92private static final int S_TIMED_WAITING = 4;93private static final int S_PARKED = 5;94private static final int S_TIMED_PARKED = 6;95private static final int S_SLEEPING = 7;96private static final int S_TERMINATE = 8;9798// for debugging99private final AtomicInteger iterations = new AtomicInteger();100private final AtomicInteger interrupted = new AtomicInteger();101102private final LockFreeLogger logger = new LockFreeLogger();103104@Override105public void run() {106// this thread has started107while (!done) {108// state transition109int nextState = state;110if (newState != state) {111nextState = newState;112iterations.set(0);113interrupted.set(0);114}115iterations.incrementAndGet();116switch (nextState) {117case S_RUNNABLE: {118stateChange(nextState);119double sum = 0;120for (int i = 0; i < 1000; i++) {121double r = Math.random();122double x = Math.pow(3, r);123sum += x - r;124}125break;126}127case S_BLOCKED: {128log("%d: %s is going to block (iterations %d)%n",129getId(), getName(), iterations.get());130stateChange(nextState);131// going to block on lock132synchronized (lock) {133log("%d: %s acquired the lock (iterations %d)%n",134getId(), getName(), iterations.get());135try {136// this thread has escaped the BLOCKED state137// release the lock and a short wait before continue138lock.wait(Utils.adjustTimeout(10));139} catch (InterruptedException e) {140// ignore141interrupted.incrementAndGet();142}143}144break;145}146case S_WAITING: {147synchronized (lock) {148log("%d: %s is going to waiting (iterations %d interrupted %d)%n",149getId(), getName(), iterations.get(), interrupted.get());150try {151stateChange(nextState);152lock.wait();153log("%d: %s wakes up from waiting (iterations %d interrupted %d)%n",154getId(), getName(), iterations.get(), interrupted.get());155} catch (InterruptedException e) {156// ignore157interrupted.incrementAndGet();158}159}160break;161}162case S_TIMED_WAITING: {163synchronized (lock) {164log("%d: %s is going to timed waiting (iterations %d interrupted %d)%n",165getId(), getName(), iterations.get(), interrupted.get());166try {167stateChange(nextState);168lock.wait(Integer.MAX_VALUE);169log("%d: %s wakes up from timed waiting (iterations %d interrupted %d)%n",170getId(), getName(), iterations.get(), interrupted.get());171} catch (InterruptedException e) {172// ignore173interrupted.incrementAndGet();174}175}176break;177}178case S_PARKED: {179log("%d: %s is going to park (iterations %d)%n",180getId(), getName(), iterations.get());181stateChange(nextState);182LockSupport.park();183break;184}185case S_TIMED_PARKED: {186log("%d: %s is going to timed park (iterations %d)%n",187getId(), getName(), iterations.get());188long deadline = System.currentTimeMillis() +189Utils.adjustTimeout(10000*1000);190stateChange(nextState);191LockSupport.parkUntil(deadline);192break;193}194case S_SLEEPING: {195log("%d: %s is going to sleep (iterations %d interrupted %d)%n",196getId(), getName(), iterations.get(), interrupted.get());197try {198stateChange(nextState);199Thread.sleep(Utils.adjustTimeout(1000000));200} catch (InterruptedException e) {201// finish sleeping202interrupted.incrementAndGet();203}204break;205}206case S_TERMINATE: {207done = true;208stateChange(nextState);209break;210}211default:212break;213}214}215}216217/**218* Change the state if it matches newState.219*/220private void stateChange(int nextState) {221// no state change222if (state == nextState)223return;224225// transition to the new state226if (newState == nextState) {227state = nextState;228phaser.arrive();229log("%d: state change: %s %s%n",230getId(), toStateName(nextState), phaserToString(phaser));231return;232}233234// should never reach here235throw new RuntimeException("current " + state + " next " + nextState +236" new state " + newState);237}238239/**240* Blocks until this thread transitions to the given state241*/242public void transitionTo(Thread.State tstate) throws InterruptedException {243switch (tstate) {244case RUNNABLE:245nextState(S_RUNNABLE);246break;247case BLOCKED:248nextState(S_BLOCKED);249break;250case WAITING:251nextState(S_WAITING);252break;253case TIMED_WAITING:254nextState(S_TIMED_WAITING);255break;256case TERMINATED:257nextState(S_TERMINATE);258break;259default:260break;261}262}263264/**265* Blocks until this thread transitions to sleeping266*/267public void transitionToSleep() throws InterruptedException {268nextState(S_SLEEPING);269}270271/**272* Blocks until this thread transitions to park or timed park273*/274public void transitionToPark(boolean timed) throws InterruptedException {275nextState(timed ? S_TIMED_PARKED : S_PARKED);276}277278private void nextState(int s) throws InterruptedException {279final long id = Thread.currentThread().getId();280log("%d: wait until the thread transitions to %s %s%n",281id, toStateName(s), phaserToString(phaser));282this.newState = s;283int phase = phaser.arrive();284log("%d: awaiting party arrive %s %s%n",285id, toStateName(s), phaserToString(phaser));286for (;;) {287// when this thread has changed its state before it waits or parks288// on a lock, a potential race might happen if it misses the notify289// or unpark. Hence await for the phaser to advance with timeout290// to cope with this race condition.291switch (state) {292case S_WAITING:293case S_TIMED_WAITING:294synchronized (lock) {295lock.notify();296}297break;298case S_PARKED:299case S_TIMED_PARKED:300LockSupport.unpark(this);301break;302case S_SLEEPING:303this.interrupt();304break;305case S_BLOCKED:306default:307break;308}309try {310phaser.awaitAdvanceInterruptibly(phase, 100, TimeUnit.MILLISECONDS);311log("%d: arrived at %s %s%n",312id, toStateName(s), phaserToString(phaser));313return;314} catch (TimeoutException ex) {315// this thread hasn't arrived at this phase316log("%d: Timeout: %s%n", id, phaser);317}318}319}320321private String phaserToString(Phaser p) {322return "[phase = " + p.getPhase() +323" parties = " + p.getRegisteredParties() +324" arrived = " + p.getArrivedParties() + "]";325}326327private String toStateName(int state) {328switch (state) {329case S_RUNNABLE:330return "runnable";331case S_WAITING:332return "waiting";333case S_TIMED_WAITING:334return "timed waiting";335case S_PARKED:336return "parked";337case S_TIMED_PARKED:338return "timed parked";339case S_SLEEPING:340return "sleeping";341case S_BLOCKED:342return "blocked";343case S_TERMINATE:344return "terminated";345default:346return "unknown " + state;347}348}349350private void log(String msg, Object ... params) {351logger.log(msg, params);352}353354/**355* Waits for the controller to complete the test run and returns the356* generated log357* @return The controller log358* @throws InterruptedException359*/360public String getLog() throws InterruptedException {361return getLog(0);362}363364/**365* Waits at most {@code millis} milliseconds for the controller366* to complete the test run and returns the generated log.367* A timeout of {@code 0} means to wait forever.368*/369public String getLog(long millis) throws InterruptedException {370this.join(millis);371372return logger.toString();373}374}375376377