1 /*
2 * Copyright (c) 2015, 2018, 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 * @summary Call Object.wait() method. Check that monitor information
27 * presented in the stack is correct. Call notifyAll method
28 * monitor info have to disappear from the stack.
29 * Repeats the same scenario calling interrupt() method
30 * @modules java.base/jdk.internal.misc
31 * @library /test/lib
32 * @library ../share
33 * @run main/othervm -XX:+UsePerfData WaitNotifyThreadTest
34 */
35 import common.ToolResults;
36 import java.util.Iterator;
37 import utils.*;
38
39 public class WaitNotifyThreadTest {
40
41 private Object monitor = new Object();
42 private final String OBJECT = "a java.lang.Object";
43 private final String OBJECT_WAIT = "java.lang.Object.wait";
44 private final String RUN_METHOD = "WaitNotifyThreadTest$WaitThread.run";
45
46 interface Action {
47 void doAction(Thread thread);
48 }
49
50 class ActionNotify implements Action {
51
52 @Override
53 public void doAction(Thread thread) {
54 // Notify the waiting thread, so it stops waiting and sleeps
55 synchronized (monitor) {
56 monitor.notifyAll();
57 }
58 // Wait until MyWaitingThread exits the monitor and sleeps
59 while (thread.getState() != Thread.State.TIMED_WAITING) {}
60 }
61 }
62
63 class ActionInterrupt implements Action {
64
65 @Override
66 public void doAction(Thread thread) {
67 // Interrupt the thread
68 thread.interrupt();
69 // Wait until MyWaitingThread exits the monitor and sleeps
70 while (thread.getState() != Thread.State.TIMED_WAITING) {}
71 }
72 }
73
74 class WaitThread extends Thread {
75
76 @Override
77 public void run() {
78 try {
79 synchronized (monitor) {
80 monitor.wait();
81 }
82 } catch (InterruptedException x) {
83
84 }
85 Utils.sleep();
86 }
87 }
88
89 public static void main(String[] args) throws Exception {
90 new WaitNotifyThreadTest().doTest();
91 }
92
93 private void doTest() throws Exception {
94
95 // Verify stack trace consistency when notifying the thread
96 doTest(new ActionNotify());
97
98 // Verify stack trace consistency when interrupting the thread
99 doTest(new ActionInterrupt());
100 }
101
102 private void doTest(Action action) throws Exception {
103
104 final String WAITING_THREAD_NAME = "MyWaitingThread";
105
106 // Start a thread that just waits
107 WaitThread waitThread = new WaitThread();
108 waitThread.setName(WAITING_THREAD_NAME);
109 waitThread.start();
110 // Wait until MyWaitingThread enters the monitor
111 while (waitThread.getState() != Thread.State.WAITING) {}
112
113 // Collect output from the jstack tool
114 JstackTool jstackTool = new JstackTool(ProcessHandle.current().pid());
115 ToolResults results = jstackTool.measure();
116
117 // Analyze the jstack output for the patterns needed
118 JStack jstack1 = new DefaultFormat().parse(results.getStdoutString());
119 ThreadStack ti1 = jstack1.getThreadStack(WAITING_THREAD_NAME);
120 analyzeThreadStackWaiting(ti1);
121
122 action.doAction(waitThread);
123
124 // Collect output from the jstack tool again
125 results = jstackTool.measure();
126
127 // Analyze the output again
128 JStack jstack2 = new DefaultFormat().parse(results.getStdoutString());
129 ThreadStack ti2 = jstack2.getThreadStack(WAITING_THREAD_NAME);
130 analyzeThreadStackNoWaiting(ti2);
131 }
132
133 private void analyzeThreadStackWaiting(ThreadStack ti1) {
134 Iterator<MethodInfo> it = ti1.getStack().iterator();
135
136 String monitorAddress = null;
137 while (it.hasNext()) {
138 MethodInfo mi = it.next();
139 if (mi.getName().startsWith(OBJECT_WAIT) && mi.getCompilationUnit() == null /*native method*/) {
140 if (mi.getLocks().size() == 1) {
141 MonitorInfo monInfo = mi.getLocks().getFirst();
142 monitorAddress = monInfo.getMonitorAddress();
143 assertMonitorInfo("waiting on", monInfo, monitorAddress, OBJECT_WAIT);
144 } else {
145 throw new RuntimeException(OBJECT_WAIT + " method has to contain one lock record but it contains "
146 + mi.getLocks().size());
147 }
148 }
149
150 if (mi.getName().startsWith(RUN_METHOD)) {
151 if (monitorAddress == null) {
152 throw new RuntimeException("Cannot found monitor info associated with " + OBJECT_WAIT + " method");
153 }
154 if (mi.getLocks().size() == 1) {
155 MonitorInfo monInfo = mi.getLocks().getLast();
156 if (monitorAddress.equals("no object reference available")) {
157 monitorAddress = monInfo.getMonitorAddress();
158 }
159 assertMonitorInfo("locked", monInfo, monitorAddress, RUN_METHOD);
160 }
161 else {
162 throw new RuntimeException(RUN_METHOD + " method has to contain one lock record but it contains "
163 + mi.getLocks().size());
164 }
165 }
166 }
167 }
168
169 private void assertMonitorInfo(String expectedMessage, MonitorInfo monInfo, String monitorAddress, String method) {
170 if (monInfo.getType().equals(expectedMessage)
171 && compareMonitorClass(monInfo)
172 && monInfo.getMonitorAddress().equals(
173 monitorAddress)) {
174 System.out.println("Correct monitor info found in " + method + " method");
175 } else {
176 System.err.println("Error: incorrect monitor info: " + monInfo.getType() + ", " + monInfo.getMonitorClass() + ", " + monInfo.getMonitorAddress());
177 System.err.println("Expected: " + expectedMessage + ", a java.lang.Object, " + monitorAddress);
178 throw new RuntimeException("Incorrect lock record in " + method + " method");
179 }
180 }
181
182 private boolean compareMonitorClass(MonitorInfo monInfo) {
183 // If monitor class info is present in the jstack output
184 // then compare it with the class of the actual monitor object
185 // If there is no monitor class info available then return true
186 return OBJECT.equals(monInfo.getMonitorClass()) || (monInfo.getMonitorClass() == null);
187 }
188
189 private void analyzeThreadStackNoWaiting(ThreadStack ti2) {
190 Iterator<MethodInfo> it = ti2.getStack().iterator();
191
192 while (it.hasNext()) {
193 MethodInfo mi = it.next();
194 if (mi.getLocks().size() != 0) {
195 throw new RuntimeException("Unexpected lock record in "
196 + mi.getName() + " method");
197 }
198 }
199 }
200
201 }
--- EOF ---