1 /* 2 * Copyright (c) 2001, 2021, 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 4413752 8262881 27 * @summary Test SuspendThread with ObjectMonitor wait. 28 * @requires vm.jvmti 29 * @library /test/lib 30 * @compile SuspendWithObjectMonitorWait.java 31 * @run main/othervm/native -agentlib:SuspendWithObjectMonitorWait SuspendWithObjectMonitorWait 32 */ 33 34 import java.io.PrintStream; 35 36 // 37 // main waiter resumer 38 // ================= ================== =================== 39 // launch waiter 40 // <launch returns> waiter running 41 // launch resumer enter threadLock 42 // <launch returns> threadLock.wait() resumer running 43 // enter threadLock : wait for notify 44 // threadLock.notify wait finishes : 45 // : reenter blocks : 46 // suspend waiter <suspended> : 47 // exit threadLock : : 48 // <ready to test> : : 49 // : : : 50 // notify resumer : wait finishes 51 // join resumer : enter threadLock 52 // : <resumed> resume waiter 53 // : : exit threadLock 54 // : reenter threadLock : 55 // <join returns> : resumer exits 56 // join waiter : 57 // <join returns> waiter exits 58 // 59 60 public class SuspendWithObjectMonitorWait { 61 private static final String AGENT_LIB = "SuspendWithObjectMonitorWait"; 62 private static final int exit_delta = 95; 63 64 private static final int DEF_TIME_MAX = 60; // default max # secs to test 65 private static final int JOIN_MAX = 30; // max # secs to wait for join 66 67 public static final int TS_INIT = 1; // initial testState 68 public static final int TS_WAITER_RUNNING = 2; // waiter is running 69 public static final int TS_RESUMER_RUNNING = 3; // resumer is running 70 public static final int TS_READY_TO_NOTIFY = 4; // ready to notify threadLock 71 public static final int TS_CALL_SUSPEND = 5; // call suspend on contender 72 public static final int TS_READY_TO_RESUME = 6; // ready to resume waiter 73 public static final int TS_CALL_RESUME = 7; // call resume on waiter 74 public static final int TS_WAITER_DONE = 8; // waiter has run; done 75 76 public static Object barrierLaunch = new Object(); // controls thread launch 77 public static Object barrierResumer = new Object(); // controls resumer 78 public static Object threadLock = new Object(); // testing object 79 80 public static long count = 0; 81 public static boolean printDebug = false; 82 public volatile static int testState; 83 log(String msg)84 private static void log(String msg) { System.out.println(msg); } 85 suspendThread(SuspendWithObjectMonitorWaitWorker thr)86 native static int suspendThread(SuspendWithObjectMonitorWaitWorker thr); wait4ContendedEnter(SuspendWithObjectMonitorWaitWorker thr)87 native static int wait4ContendedEnter(SuspendWithObjectMonitorWaitWorker thr); 88 main(String[] args)89 public static void main(String[] args) throws Exception { 90 try { 91 System.loadLibrary(AGENT_LIB); 92 log("Loaded library: " + AGENT_LIB); 93 } catch (UnsatisfiedLinkError ule) { 94 log("Failed to load library: " + AGENT_LIB); 95 log("java.library.path: " + System.getProperty("java.library.path")); 96 throw ule; 97 } 98 99 int timeMax = 0; 100 if (args.length == 0) { 101 timeMax = DEF_TIME_MAX; 102 } else { 103 int argIndex = 0; 104 int argsLeft = args.length; 105 if (args[0].equals("-p")) { 106 printDebug = true; 107 argIndex = 1; 108 argsLeft--; 109 } 110 if (argsLeft == 0) { 111 timeMax = DEF_TIME_MAX; 112 } else if (argsLeft == 1) { 113 try { 114 timeMax = Integer.parseUnsignedInt(args[argIndex]); 115 } catch (NumberFormatException nfe) { 116 System.err.println("'" + args[argIndex] + 117 "': invalid timeMax value."); 118 usage(); 119 } 120 } else { 121 usage(); 122 } 123 } 124 125 System.exit(run(timeMax, System.out) + exit_delta); 126 } 127 logDebug(String mesg)128 public static void logDebug(String mesg) { 129 if (printDebug) { 130 System.err.println(Thread.currentThread().getName() + ": " + mesg); 131 } 132 } 133 usage()134 public static void usage() { 135 System.err.println("Usage: " + AGENT_LIB + " [-p][time_max]"); 136 System.err.println("where:"); 137 System.err.println(" -p ::= print debug info"); 138 System.err.println(" time_max ::= max looping time in seconds"); 139 System.err.println(" (default is " + DEF_TIME_MAX + 140 " seconds)"); 141 System.exit(1); 142 } 143 run(int timeMax, PrintStream out)144 public static int run(int timeMax, PrintStream out) { 145 return (new SuspendWithObjectMonitorWait()).doWork(timeMax, out); 146 } 147 checkTestState(int exp)148 public static void checkTestState(int exp) { 149 if (testState != exp) { 150 System.err.println("Failure at " + count + " loops."); 151 throw new InternalError("Unexpected test state value: " 152 + "expected=" + exp + " actual=" + testState); 153 } 154 } 155 doWork(int timeMax, PrintStream out)156 public int doWork(int timeMax, PrintStream out) { 157 SuspendWithObjectMonitorWaitWorker waiter; // waiter thread 158 SuspendWithObjectMonitorWaitWorker resumer; // resumer thread 159 160 System.out.println("About to execute for " + timeMax + " seconds."); 161 162 long start_time = System.currentTimeMillis(); 163 while (System.currentTimeMillis() < start_time + (timeMax * 1000)) { 164 count++; 165 testState = TS_INIT; // starting the test loop 166 167 // launch the waiter thread 168 synchronized (barrierLaunch) { 169 waiter = new SuspendWithObjectMonitorWaitWorker("waiter"); 170 waiter.start(); 171 172 while (testState != TS_WAITER_RUNNING) { 173 try { 174 barrierLaunch.wait(0); // wait until it is running 175 } catch (InterruptedException ex) { 176 } 177 } 178 } 179 180 // launch the resumer thread 181 synchronized (barrierLaunch) { 182 resumer = new SuspendWithObjectMonitorWaitWorker("resumer", waiter); 183 resumer.start(); 184 185 while (testState != TS_RESUMER_RUNNING) { 186 try { 187 barrierLaunch.wait(0); // wait until it is running 188 } catch (InterruptedException ex) { 189 } 190 } 191 } 192 193 checkTestState(TS_RESUMER_RUNNING); 194 195 // The waiter thread was synchronized on threadLock before it 196 // set TS_WAITER_RUNNING and notified barrierLaunch above so 197 // we cannot enter threadLock until the waiter thread calls 198 // threadLock.wait(). 199 synchronized (threadLock) { 200 // notify waiter thread so it can try to reenter threadLock 201 testState = TS_READY_TO_NOTIFY; 202 threadLock.notify(); 203 204 // wait for the waiter thread to block 205 logDebug("before contended enter wait"); 206 int retCode = wait4ContendedEnter(waiter); 207 if (retCode != 0) { 208 throw new RuntimeException("error in JVMTI GetThreadState: " 209 + "retCode=" + retCode); 210 } 211 logDebug("done contended enter wait"); 212 213 checkTestState(TS_READY_TO_NOTIFY); 214 testState = TS_CALL_SUSPEND; 215 logDebug("before suspend thread"); 216 retCode = suspendThread(waiter); 217 if (retCode != 0) { 218 throw new RuntimeException("error in JVMTI SuspendThread: " 219 + "retCode=" + retCode); 220 } 221 logDebug("suspended thread"); 222 } 223 224 // 225 // At this point, all of the child threads are running 226 // and we can get to meat of the test: 227 // 228 // - suspended threadLock waiter (trying to reenter) 229 // - a threadLock enter in the resumer thread 230 // - resumption of the waiter thread 231 // - a threadLock enter in the freshly resumed waiter thread 232 // 233 234 synchronized (barrierResumer) { 235 checkTestState(TS_CALL_SUSPEND); 236 237 // tell resumer thread to resume waiter thread 238 testState = TS_READY_TO_RESUME; 239 barrierResumer.notify(); 240 241 // Can't call checkTestState() here because the 242 // resumer thread may have already resumed the 243 // waiter thread. 244 } 245 246 try { 247 resumer.join(JOIN_MAX * 1000); 248 if (resumer.isAlive()) { 249 System.err.println("Failure at " + count + " loops."); 250 throw new InternalError("resumer thread is stuck"); 251 } 252 waiter.join(JOIN_MAX * 1000); 253 if (waiter.isAlive()) { 254 System.err.println("Failure at " + count + " loops."); 255 throw new InternalError("waiter thread is stuck"); 256 } 257 } catch (InterruptedException ex) { 258 } 259 260 checkTestState(TS_WAITER_DONE); 261 } 262 263 System.out.println("Executed " + count + " loops in " + timeMax + 264 " seconds."); 265 266 return 0; 267 } 268 } 269 270 class SuspendWithObjectMonitorWaitWorker extends Thread { 271 private SuspendWithObjectMonitorWaitWorker target; // target for resume operation 272 SuspendWithObjectMonitorWaitWorker(String name)273 public SuspendWithObjectMonitorWaitWorker(String name) { 274 super(name); 275 } 276 SuspendWithObjectMonitorWaitWorker(String name, SuspendWithObjectMonitorWaitWorker target)277 public SuspendWithObjectMonitorWaitWorker(String name, SuspendWithObjectMonitorWaitWorker target) { 278 super(name); 279 this.target = target; 280 } 281 resumeThread(SuspendWithObjectMonitorWaitWorker thr)282 native static int resumeThread(SuspendWithObjectMonitorWaitWorker thr); 283 run()284 public void run() { 285 SuspendWithObjectMonitorWait.logDebug("thread running"); 286 287 // 288 // Launch the waiter thread: 289 // - grab the threadLock 290 // - threadLock.wait() 291 // - releases threadLock 292 // 293 if (getName().equals("waiter")) { 294 // grab threadLock before we tell main we are running 295 SuspendWithObjectMonitorWait.logDebug("before enter threadLock"); 296 synchronized(SuspendWithObjectMonitorWait.threadLock) { 297 SuspendWithObjectMonitorWait.logDebug("enter threadLock"); 298 299 SuspendWithObjectMonitorWait.checkTestState(SuspendWithObjectMonitorWait.TS_INIT); 300 301 synchronized(SuspendWithObjectMonitorWait.barrierLaunch) { 302 // tell main we are running 303 SuspendWithObjectMonitorWait.testState = SuspendWithObjectMonitorWait.TS_WAITER_RUNNING; 304 SuspendWithObjectMonitorWait.barrierLaunch.notify(); 305 } 306 307 SuspendWithObjectMonitorWait.logDebug("before wait"); 308 309 // TS_READY_TO_NOTIFY is set after the main thread has 310 // entered threadLock so a spurious wakeup can't get the 311 // waiter thread out of this threadLock.wait(0) call: 312 while (SuspendWithObjectMonitorWait.testState <= SuspendWithObjectMonitorWait.TS_READY_TO_NOTIFY) { 313 try { 314 SuspendWithObjectMonitorWait.threadLock.wait(0); 315 } catch (InterruptedException ex) { 316 } 317 } 318 319 SuspendWithObjectMonitorWait.logDebug("after wait"); 320 321 SuspendWithObjectMonitorWait.checkTestState(SuspendWithObjectMonitorWait.TS_CALL_RESUME); 322 SuspendWithObjectMonitorWait.testState = SuspendWithObjectMonitorWait.TS_WAITER_DONE; 323 324 SuspendWithObjectMonitorWait.logDebug("exit threadLock"); 325 } 326 } 327 // 328 // Launch the resumer thread: 329 // - tries to grab the threadLock (should not block!) 330 // - grabs threadLock 331 // - resumes the waiter thread 332 // - releases threadLock 333 // 334 else if (getName().equals("resumer")) { 335 synchronized(SuspendWithObjectMonitorWait.barrierResumer) { 336 synchronized(SuspendWithObjectMonitorWait.barrierLaunch) { 337 // tell main we are running 338 SuspendWithObjectMonitorWait.testState = SuspendWithObjectMonitorWait.TS_RESUMER_RUNNING; 339 SuspendWithObjectMonitorWait.barrierLaunch.notify(); 340 } 341 SuspendWithObjectMonitorWait.logDebug("thread waiting"); 342 while (SuspendWithObjectMonitorWait.testState != SuspendWithObjectMonitorWait.TS_READY_TO_RESUME) { 343 try { 344 // wait for main to tell us when to continue 345 SuspendWithObjectMonitorWait.barrierResumer.wait(0); 346 } catch (InterruptedException ex) { 347 } 348 } 349 } 350 351 SuspendWithObjectMonitorWait.logDebug("before enter threadLock"); 352 synchronized(SuspendWithObjectMonitorWait.threadLock) { 353 SuspendWithObjectMonitorWait.logDebug("enter threadLock"); 354 355 SuspendWithObjectMonitorWait.checkTestState(SuspendWithObjectMonitorWait.TS_READY_TO_RESUME); 356 SuspendWithObjectMonitorWait.testState = SuspendWithObjectMonitorWait.TS_CALL_RESUME; 357 358 // resume the waiter thread so waiter.join() can work 359 SuspendWithObjectMonitorWait.logDebug("before resume thread"); 360 int retCode = resumeThread(target); 361 if (retCode != 0) { 362 throw new RuntimeException("error in JVMTI ResumeThread: " + 363 "retCode=" + retCode); 364 } 365 SuspendWithObjectMonitorWait.logDebug("resumed thread"); 366 367 SuspendWithObjectMonitorWait.logDebug("exit threadLock"); 368 } 369 } 370 } 371 } 372