1 /* 2 * Copyright (c) 2015, 2022, 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.wait0"; 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 }