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 enter. 28 * @requires vm.jvmti 29 * @library /test/lib 30 * @compile SuspendWithObjectMonitorEnter.java 31 * @run main/othervm/native -agentlib:SuspendWithObjectMonitorEnter SuspendWithObjectMonitorEnter 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 SuspendWithObjectMonitorEnter { 59 private static final String AGENT_LIB = "SuspendWithObjectMonitorEnter"; 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 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(SuspendWithObjectMonitorEnterWorker thr)86 native static int suspendThread(SuspendWithObjectMonitorEnterWorker thr); wait4ContendedEnter(SuspendWithObjectMonitorEnterWorker thr)87 native static int wait4ContendedEnter(SuspendWithObjectMonitorEnterWorker 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 SuspendWithObjectMonitorEnter()).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 SuspendWithObjectMonitorEnterWorker blocker; // blocker thread 158 SuspendWithObjectMonitorEnterWorker contender; // contender thread 159 SuspendWithObjectMonitorEnterWorker resumer; // resumer thread 160 161 System.out.println("About to execute for " + timeMax + " seconds."); 162 163 long start_time = System.currentTimeMillis(); 164 while (System.currentTimeMillis() < start_time + (timeMax * 1000)) { 165 count++; 166 testState = TS_INIT; // starting the test loop 167 168 // launch the blocker thread 169 synchronized (barrierLaunch) { 170 blocker = new SuspendWithObjectMonitorEnterWorker("blocker"); 171 blocker.start(); 172 173 while (testState != TS_BLOCKER_RUNNING) { 174 try { 175 barrierLaunch.wait(0); // wait until it is running 176 } catch (InterruptedException ex) { 177 } 178 } 179 } 180 181 // launch the contender thread 182 synchronized (barrierLaunch) { 183 contender = new SuspendWithObjectMonitorEnterWorker("contender"); 184 contender.start(); 185 186 while (testState != TS_CONTENDER_RUNNING) { 187 try { 188 barrierLaunch.wait(0); // wait until it is running 189 } catch (InterruptedException ex) { 190 } 191 } 192 } 193 194 // launch the resumer thread 195 synchronized (barrierLaunch) { 196 resumer = new SuspendWithObjectMonitorEnterWorker("resumer", contender); 197 resumer.start(); 198 199 while (testState != TS_RESUMER_RUNNING) { 200 try { 201 barrierLaunch.wait(0); // wait until it is running 202 } catch (InterruptedException ex) { 203 } 204 } 205 } 206 207 // wait for the contender thread to block 208 logDebug("before contended enter wait"); 209 int retCode = wait4ContendedEnter(contender); 210 if (retCode != 0) { 211 throw new RuntimeException("error in JVMTI GetThreadState: " + 212 "retCode=" + retCode); 213 } 214 logDebug("done contended enter wait"); 215 216 checkTestState(TS_RESUMER_RUNNING); 217 testState = TS_CALL_SUSPEND; 218 logDebug("before suspend thread"); 219 retCode = suspendThread(contender); 220 if (retCode != 0) { 221 throw new RuntimeException("error in JVMTI SuspendThread: " + 222 "retCode=" + retCode); 223 } 224 logDebug("suspended thread"); 225 226 // 227 // At this point, all of the child threads are running 228 // and we can get to meat of the test: 229 // 230 // - suspended threadLock contender 231 // - a threadLock exit in the blocker thread 232 // - a threadLock enter in the resumer thread 233 // - resumption of the contender thread 234 // - a threadLock enter in the freshly resumed contender thread 235 // 236 synchronized (barrierBlocker) { 237 checkTestState(TS_CALL_SUSPEND); 238 239 // tell blocker thread to exit threadLock 240 testState = TS_DONE_BLOCKING; 241 barrierBlocker.notify(); 242 } 243 244 synchronized (barrierResumer) { 245 // tell resumer thread to resume contender thread 246 testState = TS_READY_TO_RESUME; 247 barrierResumer.notify(); 248 249 // Can't call checkTestState() here because the 250 // resumer thread may have already resumed the 251 // contender thread. 252 } 253 254 try { 255 blocker.join(); 256 resumer.join(JOIN_MAX * 1000); 257 if (resumer.isAlive()) { 258 System.err.println("Failure at " + count + " loops."); 259 throw new InternalError("resumer thread is stuck"); 260 } 261 contender.join(JOIN_MAX * 1000); 262 if (contender.isAlive()) { 263 System.err.println("Failure at " + count + " loops."); 264 throw new InternalError("contender thread is stuck"); 265 } 266 } catch (InterruptedException ex) { 267 } 268 269 checkTestState(TS_CONTENDER_DONE); 270 } 271 272 System.out.println("Executed " + count + " loops in " + timeMax + 273 " seconds."); 274 275 return 0; 276 } 277 } 278 279 class SuspendWithObjectMonitorEnterWorker extends Thread { 280 private SuspendWithObjectMonitorEnterWorker target; // target for resume operation 281 SuspendWithObjectMonitorEnterWorker(String name)282 public SuspendWithObjectMonitorEnterWorker(String name) { 283 super(name); 284 } 285 SuspendWithObjectMonitorEnterWorker(String name, SuspendWithObjectMonitorEnterWorker target)286 public SuspendWithObjectMonitorEnterWorker(String name, SuspendWithObjectMonitorEnterWorker target) { 287 super(name); 288 this.target = target; 289 } 290 resumeThread(SuspendWithObjectMonitorEnterWorker thr)291 native static int resumeThread(SuspendWithObjectMonitorEnterWorker thr); 292 run()293 public void run() { 294 SuspendWithObjectMonitorEnter.logDebug("thread running"); 295 296 // 297 // Launch the blocker thread: 298 // - grabs threadLock 299 // - holds threadLock until we tell it let go 300 // - releases threadLock 301 // 302 if (getName().equals("blocker")) { 303 // grab threadLock before we tell main we are running 304 SuspendWithObjectMonitorEnter.logDebug("before enter threadLock"); 305 synchronized(SuspendWithObjectMonitorEnter.threadLock) { 306 SuspendWithObjectMonitorEnter.logDebug("enter threadLock"); 307 308 SuspendWithObjectMonitorEnter.checkTestState(SuspendWithObjectMonitorEnter.TS_INIT); 309 310 synchronized(SuspendWithObjectMonitorEnter.barrierBlocker) { 311 synchronized(SuspendWithObjectMonitorEnter.barrierLaunch) { 312 // tell main we are running 313 SuspendWithObjectMonitorEnter.testState = SuspendWithObjectMonitorEnter.TS_BLOCKER_RUNNING; 314 SuspendWithObjectMonitorEnter.barrierLaunch.notify(); 315 } 316 SuspendWithObjectMonitorEnter.logDebug("thread waiting"); 317 // TS_READY_TO_RESUME is set right after TS_DONE_BLOCKING 318 // is set so either can get the blocker thread out of 319 // this wait() wrapper: 320 while (SuspendWithObjectMonitorEnter.testState != SuspendWithObjectMonitorEnter.TS_DONE_BLOCKING && 321 SuspendWithObjectMonitorEnter.testState != SuspendWithObjectMonitorEnter.TS_READY_TO_RESUME) { 322 try { 323 // wait for main to tell us when to exit threadLock 324 SuspendWithObjectMonitorEnter.barrierBlocker.wait(0); 325 } catch (InterruptedException ex) { 326 } 327 } 328 } 329 SuspendWithObjectMonitorEnter.logDebug("exit threadLock"); 330 } 331 } 332 // 333 // Launch the contender thread: 334 // - tries to grab the threadLock 335 // - grabs threadLock 336 // - releases threadLock 337 // 338 else if (getName().equals("contender")) { 339 synchronized(SuspendWithObjectMonitorEnter.barrierLaunch) { 340 // tell main we are running 341 SuspendWithObjectMonitorEnter.testState = SuspendWithObjectMonitorEnter.TS_CONTENDER_RUNNING; 342 SuspendWithObjectMonitorEnter.barrierLaunch.notify(); 343 } 344 345 SuspendWithObjectMonitorEnter.logDebug("before enter threadLock"); 346 synchronized(SuspendWithObjectMonitorEnter.threadLock) { 347 SuspendWithObjectMonitorEnter.logDebug("enter threadLock"); 348 349 SuspendWithObjectMonitorEnter.checkTestState(SuspendWithObjectMonitorEnter.TS_CALL_RESUME); 350 SuspendWithObjectMonitorEnter.testState = SuspendWithObjectMonitorEnter.TS_CONTENDER_DONE; 351 352 SuspendWithObjectMonitorEnter.logDebug("exit threadLock"); 353 } 354 } 355 // 356 // Launch the resumer thread: 357 // - tries to grab the threadLock (should not block!) 358 // - grabs threadLock 359 // - resumes the contended thread 360 // - releases threadLock 361 // 362 else if (getName().equals("resumer")) { 363 synchronized(SuspendWithObjectMonitorEnter.barrierResumer) { 364 synchronized(SuspendWithObjectMonitorEnter.barrierLaunch) { 365 // tell main we are running 366 SuspendWithObjectMonitorEnter.testState = SuspendWithObjectMonitorEnter.TS_RESUMER_RUNNING; 367 SuspendWithObjectMonitorEnter.barrierLaunch.notify(); 368 } 369 SuspendWithObjectMonitorEnter.logDebug("thread waiting"); 370 while (SuspendWithObjectMonitorEnter.testState != SuspendWithObjectMonitorEnter.TS_READY_TO_RESUME) { 371 try { 372 // wait for main to tell us when to continue 373 SuspendWithObjectMonitorEnter.barrierResumer.wait(0); 374 } catch (InterruptedException ex) { 375 } 376 } 377 } 378 379 SuspendWithObjectMonitorEnter.logDebug("before enter threadLock"); 380 synchronized(SuspendWithObjectMonitorEnter.threadLock) { 381 SuspendWithObjectMonitorEnter.logDebug("enter threadLock"); 382 383 SuspendWithObjectMonitorEnter.checkTestState(SuspendWithObjectMonitorEnter.TS_READY_TO_RESUME); 384 SuspendWithObjectMonitorEnter.testState = SuspendWithObjectMonitorEnter.TS_CALL_RESUME; 385 386 // resume the contender thread so contender.join() can work 387 SuspendWithObjectMonitorEnter.logDebug("before resume thread"); 388 int retCode = resumeThread(target); 389 if (retCode != 0) { 390 throw new RuntimeException("error in JVMTI ResumeThread: " + 391 "retCode=" + retCode); 392 } 393 SuspendWithObjectMonitorEnter.logDebug("resumed thread"); 394 395 SuspendWithObjectMonitorEnter.logDebug("exit threadLock"); 396 } 397 } 398 } 399 } 400