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 * @library /test/lib
35 *
36 * @build jdk.testlibrary.*
37 * @build ThreadMXBeanStateTest ThreadStateController
38 * @run main ThreadMXBeanStateTest
39 */
40
41import java.lang.management.ManagementFactory;
42import java.lang.management.ThreadMXBean;
43import java.lang.management.ThreadInfo;
44import static java.lang.Thread.State.*;
45
46public 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}
237