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