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