1 /*
   2  * Copyright (c) 2003, 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 /*
  25  * @test
  26  * @bug     4967283 5080203 8022208
  27  * @summary Basic unit test of thread states returned by
  28  *          ThreadMXBean.getThreadInfo.getThreadState().
  29  *          It also tests lock information returned by ThreadInfo.
  30  * @author  Mandy Chung
  31  *
  32  * @library ../../Thread
  33  * @library /lib/testlibrary
  34  *
  35  * @build jdk.testlibrary.*
  36  * @build ThreadMXBeanStateTest ThreadStateController
  37  * @run main ThreadMXBeanStateTest
  38  */
  39 
  40 import java.lang.management.ManagementFactory;
  41 import java.lang.management.ThreadMXBean;
  42 import java.lang.management.ThreadInfo;
  43 import static java.lang.Thread.State.*;
  44 
  45 public class ThreadMXBeanStateTest {
  46     private static final ThreadMXBean tm = ManagementFactory.getThreadMXBean();
  47 
  48     static class Lock {
  49         private final String name;
  50         Lock(String name) {
  51             this.name = name;
  52         }
  53         @Override
  54         public String toString() {
  55             return name;
  56         }
  57     }
  58 
  59     private static final Lock globalLock = new Lock("my lock");
  60 
  61     public static void main(String[] argv) throws Exception {
  62         // Force thread state initialization now before the test
  63         // verification begins.
  64         Thread.currentThread().getState();
  65         ThreadStateController thread = new ThreadStateController("StateChanger", globalLock);
  66         thread.setDaemon(true);
  67         try {
  68             // before myThread starts
  69             thread.checkThreadState(NEW);
  70 
  71             thread.start();
  72             thread.transitionTo(RUNNABLE);
  73             thread.checkThreadState(RUNNABLE);
  74             checkLockInfo(thread, RUNNABLE, null, null);
  75 
  76             thread.suspend();
  77             ThreadStateController.pause(10);
  78             thread.checkThreadState(RUNNABLE);
  79             checkSuspendedThreadState(thread, RUNNABLE);
  80             thread.resume();
  81 
  82             synchronized (globalLock) {
  83                 thread.transitionTo(BLOCKED);
  84                 thread.checkThreadState(BLOCKED);
  85                 checkLockInfo(thread, BLOCKED,
  86                               globalLock, Thread.currentThread());
  87             }
  88 
  89             thread.transitionTo(WAITING);
  90             thread.checkThreadState(WAITING);
  91             checkLockInfo(thread, Thread.State.WAITING,
  92                           globalLock, null);
  93 
  94             thread.transitionTo(TIMED_WAITING);
  95             thread.checkThreadState(TIMED_WAITING);
  96             checkLockInfo(thread, TIMED_WAITING,
  97                           globalLock, null);
  98 
  99 
 100             thread.transitionToPark(true /* timed park */);
 101             thread.checkThreadState(TIMED_WAITING);
 102             checkLockInfo(thread, TIMED_WAITING, null, null);
 103 
 104             thread.transitionToPark(false /* indefinite park */);
 105             thread.checkThreadState(WAITING);
 106             checkLockInfo(thread, WAITING, null, null);
 107 
 108             thread.transitionToSleep();
 109             thread.checkThreadState(TIMED_WAITING);
 110             checkLockInfo(thread, TIMED_WAITING, null, null);
 111 
 112             thread.transitionTo(TERMINATED);
 113             thread.checkThreadState(TERMINATED);
 114         } finally {
 115             try {
 116                 System.out.println(thread.getLog());
 117             } catch (InterruptedException e) {
 118                 e.printStackTrace();
 119                 System.out.println("TEST FAILED: Unexpected exception.");
 120                 throw new RuntimeException(e);
 121             }
 122         }
 123         System.out.println("Test passed.");
 124     }
 125 
 126     private static void checkSuspendedThreadState(ThreadStateController t, Thread.State state) {
 127         ThreadInfo info = getThreadInfo(t, state);
 128         if (info == null) {
 129             throw new RuntimeException(t.getName() +
 130                " expected to have ThreadInfo " +
 131                " but got null.");
 132         }
 133 
 134         if (info.getThreadState() != state) {
 135             throw new RuntimeException(t.getName() + " expected to be in " +
 136                 state + " state but got " + info.getThreadState());
 137         }
 138 
 139         if (!info.isSuspended()) {
 140             throw new RuntimeException(t.getName() + " expected to be suspended " +
 141                 " but isSuspended() returns " + info.isSuspended());
 142         }
 143     }
 144 
 145     private static String getLockName(Object lock) {
 146         if (lock == null) return null;
 147 
 148         return lock.getClass().getName() + '@' +
 149             Integer.toHexString(System.identityHashCode(lock));
 150     }
 151 
 152     // maximum number of retries when checking for thread state.
 153     private static final int MAX_RETRY = 500;
 154     private static ThreadInfo getThreadInfo(ThreadStateController t, Thread.State expected) {
 155         // wait for the thread to transition to the expected state.
 156         // There is a small window between the thread checking the state
 157         // and the thread actual entering that state.
 158         int retryCount=0;
 159         ThreadInfo info = tm.getThreadInfo(t.getId());
 160         while (info.getThreadState() != expected && retryCount < MAX_RETRY) {
 161             ThreadStateController.pause(10);
 162             retryCount++;
 163             info = tm.getThreadInfo(t.getId());
 164         }
 165         return info;
 166     }
 167 
 168     private static void checkLockInfo(ThreadStateController t, Thread.State state,
 169                                       Object lock, Thread owner) {
 170         ThreadInfo info = getThreadInfo(t, state);
 171         if (info == null) {
 172             throw new RuntimeException(t.getName() +
 173                " expected to have ThreadInfo " +
 174                " but got null.");
 175         }
 176 
 177         if (info.getThreadState() != state) {
 178             throw new RuntimeException(t.getName() + " expected to be in " +
 179                 state + " state but got " + info.getThreadState());
 180         }
 181 
 182         if (lock == null && info.getLockName() != null) {
 183             throw new RuntimeException(t.getName() +
 184                 " expected not to be blocked on any lock" +
 185                 " but got " + info.getLockName());
 186         }
 187         String expectedLockName = getLockName(lock);
 188         if (lock != null && info.getLockName() == null) {
 189             throw new RuntimeException(t.getName() +
 190                 " expected to be blocked on lock [" + expectedLockName +
 191                 "] but got null.");
 192         }
 193 
 194         if (lock != null && !expectedLockName.equals(info.getLockName())) {
 195             throw new RuntimeException(t.getName() +
 196                 " expected to be blocked on lock [" + expectedLockName +
 197                 "] but got [" + info.getLockName() + "].");
 198         }
 199 
 200         if (owner == null && info.getLockOwnerName() != null) {
 201             throw new RuntimeException("Lock owner is expected " +
 202                 " to be null but got " + info.getLockOwnerName());
 203         }
 204 
 205         if (owner != null && info.getLockOwnerName() == null) {
 206             throw new RuntimeException("Lock owner is expected to be " +
 207                 owner.getName() +
 208                 " but got null.");
 209         }
 210         if (owner != null && !info.getLockOwnerName().equals(owner.getName())) {
 211             throw new RuntimeException("Lock owner is expected to be " +
 212                 owner.getName() +
 213                 " but got " + owner.getName());
 214         }
 215         if (owner == null && info.getLockOwnerId() != -1) {
 216             throw new RuntimeException("Lock owner is expected " +
 217                 " to be -1 but got " + info.getLockOwnerId());
 218         }
 219 
 220         if (owner != null && info.getLockOwnerId() <= 0) {
 221             throw new RuntimeException("Lock owner is expected to be " +
 222                 owner.getName() + "(id = " + owner.getId() +
 223                 ") but got " + info.getLockOwnerId());
 224         }
 225         if (owner != null && info.getLockOwnerId() != owner.getId()) {
 226             throw new RuntimeException("Lock owner is expected to be " +
 227                 owner.getName() + "(id = " + owner.getId() +
 228                 ") but got " + info.getLockOwnerId());
 229         }
 230         if (info.isSuspended()) {
 231             throw new RuntimeException(t.getName() +
 232                 " isSuspended() returns " + info.isSuspended());
 233         }
 234     }
 235 }