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