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 27 * @summary Test SuspendThread with RawMonitor enter. 28 * @requires vm.jvmti 29 * @library /test/lib 30 * @compile SuspendWithRawMonitorEnter.java 31 * @run main/othervm/native -agentlib:SuspendWithRawMonitorEnter SuspendWithRawMonitorEnter 32 */ 33 34 import java.io.PrintStream; 35 36 // 37 // main blocker contender resumer 38 // ================= ================ =================== ================ 39 // launch blocker 40 // <launch returns> blocker running 41 // launch contender enter threadLock 42 // <launch returns> wait for notify contender running 43 // launch resumer : block on threadLock 44 // <launch returns> : : resumer running 45 // suspend contender : <suspended> wait for notify 46 // <ready to test> : : : 47 // : : : : 48 // notify blocker wait finishes : : 49 // notify resumer exit threadLock : wait finishes 50 // join blocker : : enter threadLock 51 // <join returns> blocker exits <resumed> resume contender 52 // join resumer : exit threadLock 53 // <join returns> enter threadLock resumer exits 54 // join contender exit threadLock 55 // <join returns> contender exits 56 // 57 58 public class SuspendWithRawMonitorEnter { 59 private static final String AGENT_LIB = "SuspendWithRawMonitorEnter"; 60 private static final int exit_delta = 95; 61 62 private static final int DEF_TIME_MAX = 60; // default max # secs to test 63 private static final int JOIN_MAX = 30; // max # secs to wait for join 64 65 public static final int TS_INIT = 1; // initial testState 66 public static final int TS_BLOCKER_RUNNING = 2; // blocker is running 67 public static final int TS_CONTENDER_RUNNING = 3; // contender is running 68 public static final int TS_RESUMER_RUNNING = 4; // resumer is running 69 public static final int TS_CALL_SUSPEND = 5; // call suspend on contender 70 public static final int TS_DONE_BLOCKING = 6; // done blocking threadLock 71 public static final int TS_READY_TO_RESUME = 7; // ready to resume contender 72 public static final int TS_CALL_RESUME = 8; // call resume on contender 73 public static final int TS_CONTENDER_DONE = 9; // contender has run; done 74 75 public static Object barrierLaunch = new Object(); // controls thread launch 76 public static Object barrierBlocker = new Object(); // controls blocker 77 public static Object barrierResumer = new Object(); // controls resumer 78 79 public static long count = 0; 80 public static boolean printDebug = false; 81 public volatile static int testState; 82 log(String msg)83 private static void log(String msg) { System.out.println(msg); } 84 createRawMonitor()85 native static int createRawMonitor(); destroyRawMonitor()86 native static int destroyRawMonitor(); suspendThread(SuspendWithRawMonitorEnterWorker thr)87 native static int suspendThread(SuspendWithRawMonitorEnterWorker 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 SuspendWithRawMonitorEnter()).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 SuspendWithRawMonitorEnterWorker blocker; // blocker thread 158 SuspendWithRawMonitorEnterWorker contender; // contender thread 159 SuspendWithRawMonitorEnterWorker resumer; // resumer thread 160 161 int retCode = createRawMonitor(); 162 if (retCode != 0) { 163 throw new RuntimeException("error in JVMTI CreateRawMonitor: " + 164 "retCode=" + retCode); 165 } 166 logDebug("created threadLock"); 167 168 System.out.println("About to execute for " + timeMax + " seconds."); 169 170 long start_time = System.currentTimeMillis(); 171 while (System.currentTimeMillis() < start_time + (timeMax * 1000)) { 172 count++; 173 testState = TS_INIT; // starting the test loop 174 175 // launch the blocker thread 176 synchronized (barrierLaunch) { 177 blocker = new SuspendWithRawMonitorEnterWorker("blocker"); 178 blocker.start(); 179 180 while (testState != TS_BLOCKER_RUNNING) { 181 try { 182 barrierLaunch.wait(0); // wait until it is running 183 } catch (InterruptedException ex) { 184 } 185 } 186 } 187 188 // launch the contender thread 189 synchronized (barrierLaunch) { 190 contender = new SuspendWithRawMonitorEnterWorker("contender"); 191 contender.start(); 192 193 while (testState != TS_CONTENDER_RUNNING) { 194 try { 195 barrierLaunch.wait(0); // wait until it is running 196 } catch (InterruptedException ex) { 197 } 198 } 199 } 200 201 // launch the resumer thread 202 synchronized (barrierLaunch) { 203 resumer = new SuspendWithRawMonitorEnterWorker("resumer", contender); 204 resumer.start(); 205 206 while (testState != TS_RESUMER_RUNNING) { 207 try { 208 barrierLaunch.wait(0); // wait until it is running 209 } catch (InterruptedException ex) { 210 } 211 } 212 } 213 214 // 215 // Known bug: We don't have a way of knowing when the 216 // contender thread contends on the threadLock. If we 217 // suspend it before it has blocked, then we don't really 218 // have contention. However, the resumer thread won't 219 // resume the contender thread until after it has grabbed 220 // the threadLock so we don't have a lock order problem 221 // and the test won't fall over. 222 // 223 // We reduce the size of this timing window by launching 224 // the resumer thread after the contender thread. So the 225 // contender thread has all the setup time for the resumer 226 // thread to call JVM/TI RawMonitorEnter() and block on 227 // the threadLock. 228 // 229 checkTestState(TS_RESUMER_RUNNING); 230 testState = TS_CALL_SUSPEND; 231 logDebug("before suspend thread"); 232 retCode = suspendThread(contender); 233 if (retCode != 0) { 234 throw new RuntimeException("error in JVMTI SuspendThread: " + 235 "retCode=" + retCode); 236 } 237 logDebug("suspended thread"); 238 239 // 240 // At this point, all of the child threads are running 241 // and we can get to meat of the test: 242 // 243 // - suspended threadLock contender 244 // - a threadLock exit in the blocker thread 245 // - a threadLock enter in the resumer thread 246 // - resumption of the contender thread 247 // - a threadLock enter in the freshly resumed contender thread 248 // 249 synchronized (barrierBlocker) { 250 checkTestState(TS_CALL_SUSPEND); 251 252 // tell blocker thread to exit threadLock 253 testState = TS_DONE_BLOCKING; 254 barrierBlocker.notify(); 255 } 256 257 synchronized (barrierResumer) { 258 // tell resumer thread to resume contender thread 259 testState = TS_READY_TO_RESUME; 260 barrierResumer.notify(); 261 262 // Can't call checkTestState() here because the 263 // resumer thread may have already resumed the 264 // contender thread. 265 } 266 267 try { 268 blocker.join(); 269 resumer.join(JOIN_MAX * 1000); 270 if (resumer.isAlive()) { 271 System.err.println("Failure at " + count + " loops."); 272 throw new InternalError("resumer thread is stuck"); 273 } 274 contender.join(JOIN_MAX * 1000); 275 if (contender.isAlive()) { 276 System.err.println("Failure at " + count + " loops."); 277 throw new InternalError("contender thread is stuck"); 278 } 279 } catch (InterruptedException ex) { 280 } 281 282 checkTestState(TS_CONTENDER_DONE); 283 } 284 retCode = destroyRawMonitor(); 285 if (retCode != 0) { 286 throw new RuntimeException("error in JVMTI DestroyRawMonitor: " + 287 "retCode=" + retCode); 288 } 289 logDebug("destroyed threadLock"); 290 291 System.out.println("Executed " + count + " loops in " + timeMax + 292 " seconds."); 293 294 return 0; 295 } 296 } 297 298 class SuspendWithRawMonitorEnterWorker extends Thread { 299 private SuspendWithRawMonitorEnterWorker target; // target for resume operation 300 SuspendWithRawMonitorEnterWorker(String name)301 public SuspendWithRawMonitorEnterWorker(String name) { 302 super(name); 303 } 304 SuspendWithRawMonitorEnterWorker(String name, SuspendWithRawMonitorEnterWorker target)305 public SuspendWithRawMonitorEnterWorker(String name, SuspendWithRawMonitorEnterWorker target) { 306 super(name); 307 this.target = target; 308 } 309 rawMonitorEnter()310 native static int rawMonitorEnter(); rawMonitorExit()311 native static int rawMonitorExit(); resumeThread(SuspendWithRawMonitorEnterWorker thr)312 native static int resumeThread(SuspendWithRawMonitorEnterWorker thr); 313 run()314 public void run() { 315 SuspendWithRawMonitorEnter.logDebug("thread running"); 316 317 // 318 // Launch the blocker thread: 319 // - grabs threadLock 320 // - holds threadLock until we tell it let go 321 // - releases threadLock 322 // 323 int retCode; 324 if (getName().equals("blocker")) { 325 // grab threadLock before we tell main we are running 326 SuspendWithRawMonitorEnter.logDebug("before enter threadLock"); 327 retCode = rawMonitorEnter(); 328 if (retCode != 0) { 329 throw new RuntimeException("error in JVMTI RawMonitorEnter: " + 330 "retCode=" + retCode); 331 } 332 SuspendWithRawMonitorEnter.logDebug("enter threadLock"); 333 334 SuspendWithRawMonitorEnter.checkTestState(SuspendWithRawMonitorEnter.TS_INIT); 335 336 // recursive entry 337 SuspendWithRawMonitorEnter.logDebug("before recursive enter threadLock"); 338 retCode = rawMonitorEnter(); 339 if (retCode != 0) { 340 throw new RuntimeException("error in JVMTI RawMonitorEnter: " + 341 "retCode=" + retCode); 342 } 343 SuspendWithRawMonitorEnter.logDebug("recursive enter threadLock"); 344 345 SuspendWithRawMonitorEnter.logDebug("before recursive exit threadLock"); 346 retCode = rawMonitorExit(); 347 if (retCode != 0) { 348 throw new RuntimeException("error in JVMTI RawMonitorExit: " + 349 "retCode=" + retCode); 350 } 351 SuspendWithRawMonitorEnter.logDebug("recursive exit threadLock"); 352 353 synchronized(SuspendWithRawMonitorEnter.barrierBlocker) { 354 synchronized(SuspendWithRawMonitorEnter.barrierLaunch) { 355 // tell main we are running 356 SuspendWithRawMonitorEnter.testState = SuspendWithRawMonitorEnter.TS_BLOCKER_RUNNING; 357 SuspendWithRawMonitorEnter.barrierLaunch.notify(); 358 } 359 SuspendWithRawMonitorEnter.logDebug("thread waiting"); 360 // TS_READY_TO_RESUME is set right after TS_DONE_BLOCKING 361 // is set so either can get the blocker thread out of 362 // this wait() wrapper: 363 while (SuspendWithRawMonitorEnter.testState != SuspendWithRawMonitorEnter.TS_DONE_BLOCKING && 364 SuspendWithRawMonitorEnter.testState != SuspendWithRawMonitorEnter.TS_READY_TO_RESUME) { 365 try { 366 // wait for main to tell us when to exit threadLock 367 SuspendWithRawMonitorEnter.barrierBlocker.wait(0); 368 } catch (InterruptedException ex) { 369 } 370 } 371 SuspendWithRawMonitorEnter.logDebug("before exit threadLock"); 372 retCode = rawMonitorExit(); 373 if (retCode != 0) { 374 throw new RuntimeException("error in JVMTI RawMonitorExit: " 375 + "retCode=" + retCode); 376 } 377 SuspendWithRawMonitorEnter.logDebug("exit threadLock"); 378 } 379 } 380 // 381 // Launch the contender thread: 382 // - tries to grab the threadLock 383 // - grabs threadLock 384 // - releases threadLock 385 // 386 else if (getName().equals("contender")) { 387 synchronized(SuspendWithRawMonitorEnter.barrierLaunch) { 388 // tell main we are running 389 SuspendWithRawMonitorEnter.testState = SuspendWithRawMonitorEnter.TS_CONTENDER_RUNNING; 390 SuspendWithRawMonitorEnter.barrierLaunch.notify(); 391 } 392 393 SuspendWithRawMonitorEnter.logDebug("before enter threadLock"); 394 retCode = rawMonitorEnter(); 395 if (retCode != 0) { 396 throw new RuntimeException("error in JVMTI RawMonitorEnter: " + 397 "retCode=" + retCode); 398 } 399 SuspendWithRawMonitorEnter.logDebug("enter threadLock"); 400 401 SuspendWithRawMonitorEnter.checkTestState(SuspendWithRawMonitorEnter.TS_CALL_RESUME); 402 SuspendWithRawMonitorEnter.testState = SuspendWithRawMonitorEnter.TS_CONTENDER_DONE; 403 404 SuspendWithRawMonitorEnter.logDebug("before exit threadLock"); 405 retCode = rawMonitorExit(); 406 if (retCode != 0) { 407 throw new RuntimeException("error in JVMTI RawMonitorExit: " + 408 "retCode=" + retCode); 409 } 410 SuspendWithRawMonitorEnter.logDebug("exit threadLock"); 411 } 412 // 413 // Launch the resumer thread: 414 // - tries to grab the threadLock (should not block!) 415 // - grabs threadLock 416 // - resumes the contended thread 417 // - releases threadLock 418 // 419 else if (getName().equals("resumer")) { 420 synchronized(SuspendWithRawMonitorEnter.barrierResumer) { 421 synchronized(SuspendWithRawMonitorEnter.barrierLaunch) { 422 // tell main we are running 423 SuspendWithRawMonitorEnter.testState = SuspendWithRawMonitorEnter.TS_RESUMER_RUNNING; 424 SuspendWithRawMonitorEnter.barrierLaunch.notify(); 425 } 426 SuspendWithRawMonitorEnter.logDebug("thread waiting"); 427 while (SuspendWithRawMonitorEnter.testState != SuspendWithRawMonitorEnter.TS_READY_TO_RESUME) { 428 try { 429 // wait for main to tell us when to continue 430 SuspendWithRawMonitorEnter.barrierResumer.wait(0); 431 } catch (InterruptedException ex) { 432 } 433 } 434 } 435 SuspendWithRawMonitorEnter.logDebug("before enter threadLock"); 436 retCode = rawMonitorEnter(); 437 if (retCode != 0) { 438 throw new RuntimeException("error in JVMTI RawMonitorEnter: " + 439 "retCode=" + retCode); 440 } 441 SuspendWithRawMonitorEnter.logDebug("enter threadLock"); 442 443 SuspendWithRawMonitorEnter.checkTestState(SuspendWithRawMonitorEnter.TS_READY_TO_RESUME); 444 SuspendWithRawMonitorEnter.testState = SuspendWithRawMonitorEnter.TS_CALL_RESUME; 445 446 // resume the contender thread so contender.join() can work 447 SuspendWithRawMonitorEnter.logDebug("before resume thread"); 448 retCode = resumeThread(target); 449 if (retCode != 0) { 450 throw new RuntimeException("error in JVMTI ResumeThread: " + 451 "retCode=" + retCode); 452 } 453 SuspendWithRawMonitorEnter.logDebug("resumed thread"); 454 455 SuspendWithRawMonitorEnter.logDebug("before exit threadLock"); 456 retCode = rawMonitorExit(); 457 if (retCode != 0) { 458 throw new RuntimeException("error in JVMTI RawMonitorExit: " + 459 "retCode=" + retCode); 460 } 461 SuspendWithRawMonitorEnter.logDebug("exit threadLock"); 462 } 463 } 464 } 465