diff --git a/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWait.java b/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWait.java deleted file mode 100644 index 6c6de673408..00000000000 --- a/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWait.java +++ /dev/null @@ -1,773 +0,0 @@ -/* - * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @bug 4413752 8262881 - * @summary Test SuspendThread with ObjectMonitor wait. - * @requires vm.jvmti - * @library /test/lib - * @compile SuspendWithObjectMonitorWait.java - * @run main/othervm/native -agentlib:SuspendWithObjectMonitorWait SuspendWithObjectMonitorWait 1 - */ - -/* - * @test - * @bug 4413752 8262881 - * @summary Test SuspendThread with ObjectMonitor wait. - * @requires vm.jvmti - * @library /test/lib - * @compile SuspendWithObjectMonitorWait.java - * @run main/othervm/native -agentlib:SuspendWithObjectMonitorWait SuspendWithObjectMonitorWait 2 - */ - -/* - * @test - * @bug 4413752 8262881 - * @summary Test SuspendThread with ObjectMonitor wait. - * @requires vm.jvmti - * @library /test/lib - * @compile SuspendWithObjectMonitorWait.java - * @run main/othervm/native -agentlib:SuspendWithObjectMonitorWait SuspendWithObjectMonitorWait 3 - */ - -/* - * @test - * @summary Test options parser test cases - * @requires vm.jvmti - * @library /test/lib - * @compile SuspendWithObjectMonitorWait.java - * @comment main/othervm/native -agentlib:SuspendWithObjectMonitorWait SuspendWithObjectMonitorWait - * @comment main/othervm/native -agentlib:SuspendWithObjectMonitorWait SuspendWithObjectMonitorWait -p - * @comment main/othervm/native -agentlib:SuspendWithObjectMonitorWait SuspendWithObjectMonitorWait 1 -p 5 junk - * @comment main/othervm/native -agentlib:SuspendWithObjectMonitorWait SuspendWithObjectMonitorWait junk - * @comment main/othervm/native -agentlib:SuspendWithObjectMonitorWait SuspendWithObjectMonitorWait 1 junk - * @comment main/othervm/native -agentlib:SuspendWithObjectMonitorWait SuspendWithObjectMonitorWait -p 1 junk - * @comment main/othervm/native -agentlib:SuspendWithObjectMonitorWait SuspendWithObjectMonitorWait 1 -p junk - * @comment main/othervm/native -agentlib:SuspendWithObjectMonitorWait SuspendWithObjectMonitorWait 1 junk -p - */ - -import java.io.PrintStream; - -// -// doWork1 algorithm: -// main waiter resumer -// ================= ================== =================== -// launch waiter -// waiter running -// launch resumer enter threadLock -// threadLock.wait() resumer running -// enter threadLock : wait for notify -// threadLock.notify wait finishes : -// : reenter blocks : -// suspend waiter : -// exit threadLock : : -// : : -// : : : -// notify resumer : wait finishes -// join resumer : enter threadLock -// : resume waiter -// : : exit threadLock -// : reenter threadLock : -// : resumer exits -// join waiter : -// waiter exits -// - -// -// doWork2 algorithm: -// -// main waiter resumer -// ================= ================== =================== -// launch waiter -// waiter running -// launch resumer enter threadLock -// threadLock.wait() resumer running -// enter threadLock : wait for notify -// threadLock.notify wait finishes : -// : reenter blocks : -// suspend waiter : -// : : -// : : : -// notify resumer : wait finishes -// delay 1-second : : -// exit threadLock : : -// join resumer : enter threadLock -// : resume waiter -// : : exit threadLock -// : reenter threadLock : -// : resumer exits -// join waiter : -// waiter exits -// -// Note: The sleep(1-second) in main along with the delayed exit -// of threadLock in main forces the resumer thread to reach -// "enter threadLock" and block. This difference from doWork1 -// forces the resumer thread to be contending for threadLock -// while the waiter thread is in threadLock.wait() increasing -// stress on the monitor sub-system. -// - -// -// doWork3 algorithm: -// -// main waiter resumer -// =================== ====================== =================== -// launch waiter -// waiter running -// launch resumer enter threadLock -// while !READY_TO_NOTIFY resumer running -// : threadLock.wait(1) wait for notify -// enter threadLock : : -// set READY_TO_NOTIFY : -// threadLock.notify wait finishes : -// : delay 200ms reenter blocks : -// suspend waiter : -// : : -// : : : -// notify resumer : wait finishes -// delay 1-second : : -// exit threadLock : : -// join resumer : enter threadLock -// : resume waiter -// : : exit threadLock -// : reenter threadLock : -// : resumer exits -// join waiter : -// waiter exits -// -// Note: The sleep(1-second) in main along with the delayed exit -// of threadLock in main forces the resumer thread to reach -// "enter threadLock" and block. This difference from doWork1 -// forces the resumer thread to be contending for threadLock -// while the waiter thread is in the threadLock.wait(1) tight -// loop increasing stress on the monitor sub-system. -// -// Note: sleep(200ms) here while holding the threadLock to allow the -// waiter thread's timed wait to finish before we attempt to -// suspend the waiter thread. -// - -public class SuspendWithObjectMonitorWait { - private static final String AGENT_LIB = "SuspendWithObjectMonitorWait"; - private static final int exit_delta = 95; - - private static final int DEF_TIME_MAX = 60; // default max # secs to test - private static final int JOIN_MAX = 30; // max # secs to wait for join - - public static final int TS_INIT = 1; // initial testState - public static final int TS_WAITER_RUNNING = 2; // waiter is running - public static final int TS_RESUMER_RUNNING = 3; // resumer is running - public static final int TS_READY_TO_NOTIFY = 4; // ready to notify threadLock - public static final int TS_CALL_SUSPEND = 5; // call suspend on contender - public static final int TS_READY_TO_RESUME = 6; // ready to resume waiter - public static final int TS_CALL_RESUME = 7; // call resume on waiter - public static final int TS_WAITER_DONE = 8; // waiter has run; done - - public static Object barrierLaunch = new Object(); // controls thread launch - public static Object barrierResumer = new Object(); // controls resumer - public static Object threadLock = new Object(); // testing object - - public static long count = 0; - public static boolean printDebug = false; - public volatile static int testState; - - private static void log(String msg) { System.out.println(msg); } - - native static int suspendThread(SuspendWithObjectMonitorWaitWorker thr); - native static int wait4ContendedEnter(SuspendWithObjectMonitorWaitWorker thr); - - public static void main(String[] args) throws Exception { - if (args.length == 0) { - System.err.println("Invalid number of arguments, there should be at least a test_case given."); - usage(); - } - - if (args.length > 3) { - System.err.println("Invalid number of arguments, there are too many arguments."); - usage(); - } - - try { - System.loadLibrary(AGENT_LIB); - log("Loaded library: " + AGENT_LIB); - } catch (UnsatisfiedLinkError ule) { - log("Failed to load library: " + AGENT_LIB); - log("java.library.path: " + System.getProperty("java.library.path")); - throw ule; - } - - int testCase = 0; - int timeMax = 0; - for (int argIndex = 0; argIndex < args.length; argIndex++) { - if (args[argIndex].equals("-p")) { - // Handle optional -p arg regardless of position. - printDebug = true; - continue; - } - - if (testCase == 0) { - try { - // testCase must be the first non-optional arg. - testCase = Integer.parseUnsignedInt(args[argIndex]); - } catch (NumberFormatException nfe) { - System.err.println("'" + args[argIndex] + - "': invalid test_case value."); - usage(); - } - if (testCase < 1 || testCase > 3) { - System.err.println("Invalid test_case value: '" + testCase + "'"); - usage(); - } - continue; - } - - if (argIndex < args.length) { - // timeMax is an optional arg. - try { - timeMax = Integer.parseUnsignedInt(args[argIndex]); - } catch (NumberFormatException nfe) { - System.err.println("'" + args[argIndex] + - "': invalid time_max value."); - usage(); - } - } else { - timeMax = DEF_TIME_MAX; - } - } - - if (testCase == 0) { - // Just -p was given. - System.err.println("Invalid number of arguments, no test_case given."); - usage(); - } - - System.exit(run(timeMax, System.out, testCase) + exit_delta); - } - - public static void logDebug(String mesg) { - if (printDebug) { - System.err.println(Thread.currentThread().getName() + ": " + mesg); - } - } - - public static void usage() { - System.err.println("Usage: " + AGENT_LIB + " test_case [-p] [time_max]"); - System.err.println("where:"); - System.err.println(" test_case ::= 1 | 2 | 3"); - System.err.println(" -p ::= print debug info"); - System.err.println(" time_max ::= max looping time in seconds"); - System.err.println(" (default is " + DEF_TIME_MAX + - " seconds)"); - System.exit(1); - } - - public static int run(int timeMax, PrintStream out, int testCase) { - switch (testCase) { - case 1: return (new SuspendWithObjectMonitorWait()).doWork1(timeMax, out); - case 2: return (new SuspendWithObjectMonitorWait()).doWork2(timeMax, out); - case 3: return (new SuspendWithObjectMonitorWait()).doWork3(timeMax, out); - default: throw new RuntimeException("Unknown test_case: " + testCase); - } - - } - - public static void checkTestState(int exp) { - if (testState != exp) { - System.err.println("Failure at " + count + " loops."); - throw new InternalError("Unexpected test state value: " - + "expected=" + exp + " actual=" + testState); - } - } - - // Default scenario, the resumer thread is always able to grab the threadLock once notified by the main thread. - public int doWork1(int timeMax, PrintStream out) { - SuspendWithObjectMonitorWaitWorker waiter; // waiter thread - SuspendWithObjectMonitorWaitWorker resumer; // resumer thread - - System.out.println("About to execute for " + timeMax + " seconds."); - - long start_time = System.currentTimeMillis(); - while (System.currentTimeMillis() < start_time + (timeMax * 1000)) { - count++; - testState = TS_INIT; // starting the test loop - - // launch the waiter thread - synchronized (barrierLaunch) { - waiter = new SuspendWithObjectMonitorWaitWorker("waiter"); - waiter.start(); - - while (testState != TS_WAITER_RUNNING) { - try { - barrierLaunch.wait(0); // wait until it is running - } catch (InterruptedException ex) { - } - } - } - - // launch the resumer thread - synchronized (barrierLaunch) { - resumer = new SuspendWithObjectMonitorWaitWorker("resumer", waiter); - resumer.start(); - - while (testState != TS_RESUMER_RUNNING) { - try { - barrierLaunch.wait(0); // wait until it is running - } catch (InterruptedException ex) { - } - } - } - - checkTestState(TS_RESUMER_RUNNING); - - // The waiter thread was synchronized on threadLock before it - // set TS_WAITER_RUNNING and notified barrierLaunch above so - // we cannot enter threadLock until the waiter thread calls - // threadLock.wait(). - synchronized (threadLock) { - // notify waiter thread so it can try to reenter threadLock - testState = TS_READY_TO_NOTIFY; - threadLock.notify(); - - // wait for the waiter thread to block - logDebug("before contended enter wait"); - int retCode = wait4ContendedEnter(waiter); - if (retCode != 0) { - throw new RuntimeException("error in JVMTI GetThreadState: " - + "retCode=" + retCode); - } - logDebug("done contended enter wait"); - - checkTestState(TS_READY_TO_NOTIFY); - testState = TS_CALL_SUSPEND; - logDebug("before suspend thread"); - retCode = suspendThread(waiter); - if (retCode != 0) { - throw new RuntimeException("error in JVMTI SuspendThread: " - + "retCode=" + retCode); - } - logDebug("suspended thread"); - } - - // - // At this point, all of the child threads are running - // and we can get to meat of the test: - // - // - suspended threadLock waiter (trying to reenter) - // - a threadLock enter in the resumer thread - // - resumption of the waiter thread - // - a threadLock enter in the freshly resumed waiter thread - // - - synchronized (barrierResumer) { - checkTestState(TS_CALL_SUSPEND); - - // tell resumer thread to resume waiter thread - testState = TS_READY_TO_RESUME; - barrierResumer.notify(); - - // Can't call checkTestState() here because the - // resumer thread may have already resumed the - // waiter thread. - } - - try { - resumer.join(JOIN_MAX * 1000); - if (resumer.isAlive()) { - System.err.println("Failure at " + count + " loops."); - throw new InternalError("resumer thread is stuck"); - } - waiter.join(JOIN_MAX * 1000); - if (waiter.isAlive()) { - System.err.println("Failure at " + count + " loops."); - throw new InternalError("waiter thread is stuck"); - } - } catch (InterruptedException ex) { - } - - checkTestState(TS_WAITER_DONE); - } - - System.out.println("Executed " + count + " loops in " + timeMax + - " seconds."); - - return 0; - } - - // Notify the resumer while holding the threadLock. - public int doWork2(int timeMax, PrintStream out) { - SuspendWithObjectMonitorWaitWorker waiter; // waiter thread - SuspendWithObjectMonitorWaitWorker resumer; // resumer thread - - System.out.println("About to execute for " + timeMax + " seconds."); - - long start_time = System.currentTimeMillis(); - while (System.currentTimeMillis() < start_time + (timeMax * 1000)) { - count++; - testState = TS_INIT; // starting the test loop - - // launch the waiter thread - synchronized (barrierLaunch) { - waiter = new SuspendWithObjectMonitorWaitWorker("waiter"); - waiter.start(); - - while (testState != TS_WAITER_RUNNING) { - try { - barrierLaunch.wait(0); // wait until it is running - } catch (InterruptedException ex) { - } - } - } - - // launch the resumer thread - synchronized (barrierLaunch) { - resumer = new SuspendWithObjectMonitorWaitWorker("resumer", waiter); - resumer.start(); - - while (testState != TS_RESUMER_RUNNING) { - try { - barrierLaunch.wait(0); // wait until it is running - } catch (InterruptedException ex) { - } - } - } - - checkTestState(TS_RESUMER_RUNNING); - - // The waiter thread was synchronized on threadLock before it - // set TS_WAITER_RUNNING and notified barrierLaunch above so - // we cannot enter threadLock until the waiter thread calls - // threadLock.wait(). - synchronized (threadLock) { - // notify waiter thread so it can try to reenter threadLock - testState = TS_READY_TO_NOTIFY; - threadLock.notify(); - - // wait for the waiter thread to block - logDebug("before contended enter wait"); - int retCode = wait4ContendedEnter(waiter); - if (retCode != 0) { - throw new RuntimeException("error in JVMTI GetThreadState: " - + "retCode=" + retCode); - } - logDebug("done contended enter wait"); - - checkTestState(TS_READY_TO_NOTIFY); - testState = TS_CALL_SUSPEND; - logDebug("before suspend thread"); - retCode = suspendThread(waiter); - if (retCode != 0) { - throw new RuntimeException("error in JVMTI SuspendThread: " - + "retCode=" + retCode); - } - logDebug("suspended thread"); - - // - // At this point, all of the child threads are running - // and we can get to meat of the test: - // - // - suspended threadLock waiter (trying to reenter) - // - a blocked threadLock enter in the resumer thread while the - // threadLock is held by the main thread. - // - resumption of the waiter thread - // - a threadLock enter in the freshly resumed waiter thread - // - - synchronized (barrierResumer) { - checkTestState(TS_CALL_SUSPEND); - - // tell resumer thread to resume waiter thread - testState = TS_READY_TO_RESUME; - barrierResumer.notify(); - - // Can't call checkTestState() here because the - // resumer thread may have already resumed the - // waiter thread. - } - try { - // Delay for 1-second while holding the threadLock to force the - // resumer thread to block on entering the threadLock. - Thread.sleep(1000); - } catch(Exception e) {} - } - - try { - resumer.join(JOIN_MAX * 1000); - if (resumer.isAlive()) { - System.err.println("Failure at " + count + " loops."); - throw new InternalError("resumer thread is stuck"); - } - waiter.join(JOIN_MAX * 1000); - if (waiter.isAlive()) { - System.err.println("Failure at " + count + " loops."); - throw new InternalError("waiter thread is stuck"); - } - } catch (InterruptedException ex) { - } - - checkTestState(TS_WAITER_DONE); - } - - System.out.println("Executed " + count + " loops in " + timeMax + - " seconds."); - - return 0; - } - - // Suspend on the re-entry path of wait. - public int doWork3(int timeMax, PrintStream out) { - SuspendWithObjectMonitorWaitWorker waiter; // waiter thread - SuspendWithObjectMonitorWaitWorker resumer; // resumer thread - - System.out.println("About to execute for " + timeMax + " seconds."); - - long start_time = System.currentTimeMillis(); - while (System.currentTimeMillis() < start_time + (timeMax * 1000)) { - count++; - testState = TS_INIT; // starting the test loop - - // launch the waiter thread - synchronized (barrierLaunch) { - waiter = new SuspendWithObjectMonitorWaitWorker("waiter", 100); - waiter.start(); - - while (testState != TS_WAITER_RUNNING) { - try { - barrierLaunch.wait(0); // wait until it is running - } catch (InterruptedException ex) { - } - } - } - - // launch the resumer thread - synchronized (barrierLaunch) { - resumer = new SuspendWithObjectMonitorWaitWorker("resumer", waiter); - resumer.start(); - - while (testState != TS_RESUMER_RUNNING) { - try { - barrierLaunch.wait(0); // wait until it is running - } catch (InterruptedException ex) { - } - } - } - - checkTestState(TS_RESUMER_RUNNING); - - // The waiter thread was synchronized on threadLock before it - // set TS_WAITER_RUNNING and notified barrierLaunch above so - // we cannot enter threadLock until the waiter thread calls - // threadLock.wait(). - synchronized (threadLock) { - // notify waiter thread so it can try to reenter threadLock - testState = TS_READY_TO_NOTIFY; - threadLock.notify(); - - try { - Thread.sleep(200); - } catch (Exception e) {} - - // wait for the waiter thread to block - logDebug("before contended enter wait"); - int retCode = wait4ContendedEnter(waiter); - if (retCode != 0) { - throw new RuntimeException("error in JVMTI GetThreadState: " - + "retCode=" + retCode); - } - logDebug("done contended enter wait"); - - checkTestState(TS_READY_TO_NOTIFY); - testState = TS_CALL_SUSPEND; - logDebug("before suspend thread"); - retCode = suspendThread(waiter); - if (retCode != 0) { - throw new RuntimeException("error in JVMTI SuspendThread: " - + "retCode=" + retCode); - } - logDebug("suspended thread"); - - // - // At this point, all of the child threads are running - // and we can get to meat of the test: - // - // - suspended threadLock waiter (trying to reenter) - // - a blocked threadLock enter in the resumer thread while the - // threadLock is held by the main thread. - // - resumption of the waiter thread - // - a threadLock enter in the freshly resumed waiter thread - // - - synchronized (barrierResumer) { - checkTestState(TS_CALL_SUSPEND); - - // tell resumer thread to resume waiter thread - testState = TS_READY_TO_RESUME; - barrierResumer.notify(); - - // Can't call checkTestState() here because the - // resumer thread may have already resumed the - // waiter thread. - } - try { - // Delay for 1-second while holding the threadLock to force the - // resumer thread to block on entering the threadLock. - Thread.sleep(1000); - } catch (Exception e) {} - } - - try { - resumer.join(JOIN_MAX * 1000); - if (resumer.isAlive()) { - System.err.println("Failure at " + count + " loops."); - throw new InternalError("resumer thread is stuck"); - } - waiter.join(JOIN_MAX * 1000); - if (waiter.isAlive()) { - System.err.println("Failure at " + count + " loops."); - throw new InternalError("waiter thread is stuck"); - } - } catch (InterruptedException ex) { - } - - checkTestState(TS_WAITER_DONE); - } - - System.out.println("Executed " + count + " loops in " + timeMax + - " seconds."); - - return 0; - } -} - - - -class SuspendWithObjectMonitorWaitWorker extends Thread { - private SuspendWithObjectMonitorWaitWorker target; // target for resume operation - private final long waitTimeout; - - public SuspendWithObjectMonitorWaitWorker(String name) { - super(name); - this.waitTimeout = 0; - } - - public SuspendWithObjectMonitorWaitWorker(String name, long waitTimeout) { - super(name); - this.waitTimeout = waitTimeout; - } - - public SuspendWithObjectMonitorWaitWorker(String name, SuspendWithObjectMonitorWaitWorker target) { - super(name); - this.target = target; - this.waitTimeout = 0; - } - - native static int resumeThread(SuspendWithObjectMonitorWaitWorker thr); - - public void run() { - SuspendWithObjectMonitorWait.logDebug("thread running"); - - // - // Launch the waiter thread: - // - grab the threadLock - // - threadLock.wait() - // - releases threadLock - // - if (getName().equals("waiter")) { - // grab threadLock before we tell main we are running - SuspendWithObjectMonitorWait.logDebug("before enter threadLock"); - synchronized(SuspendWithObjectMonitorWait.threadLock) { - SuspendWithObjectMonitorWait.logDebug("enter threadLock"); - - SuspendWithObjectMonitorWait.checkTestState(SuspendWithObjectMonitorWait.TS_INIT); - - synchronized(SuspendWithObjectMonitorWait.barrierLaunch) { - // tell main we are running - SuspendWithObjectMonitorWait.testState = SuspendWithObjectMonitorWait.TS_WAITER_RUNNING; - SuspendWithObjectMonitorWait.barrierLaunch.notify(); - } - - SuspendWithObjectMonitorWait.logDebug("before wait"); - - // TS_READY_TO_NOTIFY is set after the main thread has - // entered threadLock so a spurious wakeup can't get the - // waiter thread out of this threadLock.wait(0) call: - while (SuspendWithObjectMonitorWait.testState <= SuspendWithObjectMonitorWait.TS_READY_TO_NOTIFY) { - try { - SuspendWithObjectMonitorWait.threadLock.wait(waitTimeout); - } catch (InterruptedException ex) { - } - } - - SuspendWithObjectMonitorWait.logDebug("after wait"); - - SuspendWithObjectMonitorWait.checkTestState(SuspendWithObjectMonitorWait.TS_CALL_RESUME); - SuspendWithObjectMonitorWait.testState = SuspendWithObjectMonitorWait.TS_WAITER_DONE; - - SuspendWithObjectMonitorWait.logDebug("exit threadLock"); - } - } - // - // Launch the resumer thread: - // - tries to grab the threadLock (should not block with doWork1!) - // - grabs threadLock - // - resumes the waiter thread - // - releases threadLock - // - else if (getName().equals("resumer")) { - synchronized(SuspendWithObjectMonitorWait.barrierResumer) { - synchronized(SuspendWithObjectMonitorWait.barrierLaunch) { - // tell main we are running - SuspendWithObjectMonitorWait.testState = SuspendWithObjectMonitorWait.TS_RESUMER_RUNNING; - SuspendWithObjectMonitorWait.barrierLaunch.notify(); - } - SuspendWithObjectMonitorWait.logDebug("thread waiting"); - while (SuspendWithObjectMonitorWait.testState != SuspendWithObjectMonitorWait.TS_READY_TO_RESUME) { - try { - // wait for main to tell us when to continue - SuspendWithObjectMonitorWait.barrierResumer.wait(0); - } catch (InterruptedException ex) { - } - } - } - - SuspendWithObjectMonitorWait.logDebug("before enter threadLock"); - synchronized(SuspendWithObjectMonitorWait.threadLock) { - SuspendWithObjectMonitorWait.logDebug("enter threadLock"); - - SuspendWithObjectMonitorWait.checkTestState(SuspendWithObjectMonitorWait.TS_READY_TO_RESUME); - SuspendWithObjectMonitorWait.testState = SuspendWithObjectMonitorWait.TS_CALL_RESUME; - - // resume the waiter thread so waiter.join() can work - SuspendWithObjectMonitorWait.logDebug("before resume thread"); - int retCode = resumeThread(target); - if (retCode != 0) { - throw new RuntimeException("error in JVMTI ResumeThread: " + - "retCode=" + retCode); - } - SuspendWithObjectMonitorWait.logDebug("resumed thread"); - - SuspendWithObjectMonitorWait.logDebug("exit threadLock"); - } - } - } -} diff --git a/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWait1.java b/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWait1.java new file mode 100644 index 00000000000..a1985424e50 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWait1.java @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4413752 8262881 + * @summary Test SuspendThread with ObjectMonitor wait. + * @requires vm.jvmti + * @library /test/lib + * @compile SuspendWithObjectMonitorWait1.java + * @run main/othervm/native -agentlib:SuspendWithObjectMonitorWait SuspendWithObjectMonitorWait1 + */ + +import java.io.PrintStream; + +// +// doWork1 algorithm: +// main waiter resumer +// ================= ================== =================== +// launch waiter +// waiter running +// launch resumer enter threadLock +// threadLock.wait() resumer running +// enter threadLock : wait for notify +// threadLock.notify wait finishes : +// : reenter blocks : +// suspend waiter : +// exit threadLock : : +// : : +// : : : +// notify resumer : wait finishes +// join resumer : enter threadLock +// : resume waiter +// : : exit threadLock +// : reenter threadLock : +// : resumer exits +// join waiter : +// waiter exits +// + +public class SuspendWithObjectMonitorWait1 extends SuspendWithObjectMonitorWaitBase { + + public static void main(String[] args) throws Exception { + if (args.length > 2) { + System.err.println("Invalid number of arguments, there are too many arguments."); + usage(); + } + + try { + System.loadLibrary(AGENT_LIB); + log("Loaded library: " + AGENT_LIB); + } catch (UnsatisfiedLinkError ule) { + log("Failed to load library: " + AGENT_LIB); + log("java.library.path: " + System.getProperty("java.library.path")); + throw ule; + } + + int timeMax = 0; + for (int argIndex = 0; argIndex < args.length; argIndex++) { + if ("-p".equals(args[argIndex])) { + // Handle optional -p arg regardless of position. + printDebug = true; + continue; + } + + if (argIndex < args.length) { + // timeMax is an optional arg. + try { + timeMax = Integer.parseUnsignedInt(args[argIndex]); + } catch (NumberFormatException nfe) { + System.err.println("'" + args[argIndex] + + "': invalid time_max value."); + usage(); + } + } else { + timeMax = DEF_TIME_MAX; + } + } + System.exit(run(timeMax, System.out) + exit_delta); + } + + public static int run(int timeMax, PrintStream out) { + return (new SuspendWithObjectMonitorWait1()).doWork1(timeMax, out); + } + + // Default scenario, the resumer thread is always able to grab the threadLock once notified by the main thread. + public int doWork1(int timeMax, PrintStream out) { + SuspendWithObjectMonitorWaitWorker waiter; // waiter thread + SuspendWithObjectMonitorWaitWorker resumer; // resumer thread + + System.out.println("About to execute for " + timeMax + " seconds."); + + long start_time = System.currentTimeMillis(); + while (System.currentTimeMillis() < start_time + (timeMax * 1000)) { + count++; + testState = TS_INIT; // starting the test loop + + // launch the waiter thread + synchronized (barrierLaunch) { + waiter = new SuspendWithObjectMonitorWaitWorker("waiter"); + waiter.start(); + + while (testState != TS_WAITER_RUNNING) { + try { + barrierLaunch.wait(0); // wait until it is running + } catch (InterruptedException ex) { + } + } + } + + // launch the resumer thread + synchronized (barrierLaunch) { + resumer = new SuspendWithObjectMonitorWaitWorker("resumer", waiter); + resumer.start(); + + while (testState != TS_RESUMER_RUNNING) { + try { + barrierLaunch.wait(0); // wait until it is running + } catch (InterruptedException ex) { + } + } + } + + checkTestState(TS_RESUMER_RUNNING); + + // The waiter thread was synchronized on threadLock before it + // set TS_WAITER_RUNNING and notified barrierLaunch above so + // we cannot enter threadLock until the waiter thread calls + // threadLock.wait(). + synchronized (threadLock) { + // notify waiter thread so it can try to reenter threadLock + testState = TS_READY_TO_NOTIFY; + threadLock.notify(); + + // wait for the waiter thread to block + logDebug("before contended enter wait"); + int retCode = wait4ContendedEnter(waiter); + if (retCode != 0) { + throw new RuntimeException("error in JVMTI GetThreadState: " + + "retCode=" + retCode); + } + logDebug("done contended enter wait"); + + checkTestState(TS_READY_TO_NOTIFY); + testState = TS_CALL_SUSPEND; + logDebug("before suspend thread"); + retCode = suspendThread(waiter); + if (retCode != 0) { + throw new RuntimeException("error in JVMTI SuspendThread: " + + "retCode=" + retCode); + } + logDebug("suspended thread"); + } + + // + // At this point, all of the child threads are running + // and we can get to meat of the test: + // + // - suspended threadLock waiter (trying to reenter) + // - a threadLock enter in the resumer thread + // - resumption of the waiter thread + // - a threadLock enter in the freshly resumed waiter thread + // + + synchronized (barrierResumer) { + checkTestState(TS_CALL_SUSPEND); + + // tell resumer thread to resume waiter thread + testState = TS_READY_TO_RESUME; + barrierResumer.notify(); + + // Can't call checkTestState() here because the + // resumer thread may have already resumed the + // waiter thread. + } + + try { + resumer.join(JOIN_MAX * 1000); + if (resumer.isAlive()) { + System.err.println("Failure at " + count + " loops."); + throw new InternalError("resumer thread is stuck"); + } + waiter.join(JOIN_MAX * 1000); + if (waiter.isAlive()) { + System.err.println("Failure at " + count + " loops."); + throw new InternalError("waiter thread is stuck"); + } + } catch (InterruptedException ex) { + } + + checkTestState(TS_WAITER_DONE); + } + + System.out.println("Executed " + count + " loops in " + timeMax + + " seconds."); + + return 0; + } +} + + + + diff --git a/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWait2.java b/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWait2.java new file mode 100644 index 00000000000..42eb0912680 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWait2.java @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4413752 8262881 + * @summary Test SuspendThread with ObjectMonitor wait. + * @requires vm.jvmti + * @library /test/lib + * @compile SuspendWithObjectMonitorWait2.java + * @run main/othervm/native -agentlib:SuspendWithObjectMonitorWait SuspendWithObjectMonitorWait2 + */ + +import java.io.PrintStream; + +// +// doWork2 algorithm: +// +// main waiter resumer +// ================= ================== =================== +// launch waiter +// waiter running +// launch resumer enter threadLock +// threadLock.wait() resumer running +// enter threadLock : wait for notify +// threadLock.notify wait finishes : +// : reenter blocks : +// suspend waiter : +// : : +// : : : +// notify resumer : wait finishes +// delay 1-second : : +// exit threadLock : : +// join resumer : enter threadLock +// : resume waiter +// : : exit threadLock +// : reenter threadLock : +// : resumer exits +// join waiter : +// waiter exits +// +// Note: The sleep(1-second) in main along with the delayed exit +// of threadLock in main forces the resumer thread to reach +// "enter threadLock" and block. This difference from doWork1 +// forces the resumer thread to be contending for threadLock +// while the waiter thread is in threadLock.wait() increasing +// stress on the monitor sub-system. +// + +public class SuspendWithObjectMonitorWait2 extends SuspendWithObjectMonitorWaitBase { + + public static void main(String[] args) throws Exception { + if (args.length > 2) { + System.err.println("Invalid number of arguments, there are too many arguments."); + usage(); + } + + try { + System.loadLibrary(AGENT_LIB); + log("Loaded library: " + AGENT_LIB); + } catch (UnsatisfiedLinkError ule) { + log("Failed to load library: " + AGENT_LIB); + log("java.library.path: " + System.getProperty("java.library.path")); + throw ule; + } + + int timeMax = 0; + for (int argIndex = 0; argIndex < args.length; argIndex++) { + if ("-p".equals(args[argIndex])) { + // Handle optional -p arg regardless of position. + printDebug = true; + continue; + } + + if (argIndex < args.length) { + // timeMax is an optional arg. + try { + timeMax = Integer.parseUnsignedInt(args[argIndex]); + } catch (NumberFormatException nfe) { + System.err.println("'" + args[argIndex] + + "': invalid time_max value."); + usage(); + } + } else { + timeMax = DEF_TIME_MAX; + } + } + + System.exit(run(timeMax, System.out) + exit_delta); + } + + public static int run(int timeMax, PrintStream out) { + return (new SuspendWithObjectMonitorWait2()).doWork2(timeMax, out); + } + + // Notify the resumer while holding the threadLock. + public int doWork2(int timeMax, PrintStream out) { + SuspendWithObjectMonitorWaitWorker waiter; // waiter thread + SuspendWithObjectMonitorWaitWorker resumer; // resumer thread + + System.out.println("About to execute for " + timeMax + " seconds."); + + long start_time = System.currentTimeMillis(); + while (System.currentTimeMillis() < start_time + (timeMax * 1000)) { + count++; + testState = TS_INIT; // starting the test loop + + // launch the waiter thread + synchronized (barrierLaunch) { + waiter = new SuspendWithObjectMonitorWaitWorker("waiter"); + waiter.start(); + + while (testState != TS_WAITER_RUNNING) { + try { + barrierLaunch.wait(0); // wait until it is running + } catch (InterruptedException ex) { + } + } + } + + // launch the resumer thread + synchronized (barrierLaunch) { + resumer = new SuspendWithObjectMonitorWaitWorker("resumer", waiter); + resumer.start(); + + while (testState != TS_RESUMER_RUNNING) { + try { + barrierLaunch.wait(0); // wait until it is running + } catch (InterruptedException ex) { + } + } + } + + checkTestState(TS_RESUMER_RUNNING); + + // The waiter thread was synchronized on threadLock before it + // set TS_WAITER_RUNNING and notified barrierLaunch above so + // we cannot enter threadLock until the waiter thread calls + // threadLock.wait(). + synchronized (threadLock) { + // notify waiter thread so it can try to reenter threadLock + testState = TS_READY_TO_NOTIFY; + threadLock.notify(); + + // wait for the waiter thread to block + logDebug("before contended enter wait"); + int retCode = wait4ContendedEnter(waiter); + if (retCode != 0) { + throw new RuntimeException("error in JVMTI GetThreadState: " + + "retCode=" + retCode); + } + logDebug("done contended enter wait"); + + checkTestState(TS_READY_TO_NOTIFY); + testState = TS_CALL_SUSPEND; + logDebug("before suspend thread"); + retCode = suspendThread(waiter); + if (retCode != 0) { + throw new RuntimeException("error in JVMTI SuspendThread: " + + "retCode=" + retCode); + } + logDebug("suspended thread"); + + // + // At this point, all of the child threads are running + // and we can get to meat of the test: + // + // - suspended threadLock waiter (trying to reenter) + // - a blocked threadLock enter in the resumer thread while the + // threadLock is held by the main thread. + // - resumption of the waiter thread + // - a threadLock enter in the freshly resumed waiter thread + // + + synchronized (barrierResumer) { + checkTestState(TS_CALL_SUSPEND); + + // tell resumer thread to resume waiter thread + testState = TS_READY_TO_RESUME; + barrierResumer.notify(); + + // Can't call checkTestState() here because the + // resumer thread may have already resumed the + // waiter thread. + } + try { + // Delay for 1-second while holding the threadLock to force the + // resumer thread to block on entering the threadLock. + Thread.sleep(1000); + } catch(Exception e) {} + } + + try { + resumer.join(JOIN_MAX * 1000); + if (resumer.isAlive()) { + System.err.println("Failure at " + count + " loops."); + throw new InternalError("resumer thread is stuck"); + } + waiter.join(JOIN_MAX * 1000); + if (waiter.isAlive()) { + System.err.println("Failure at " + count + " loops."); + throw new InternalError("waiter thread is stuck"); + } + } catch (InterruptedException ex) { + } + + checkTestState(TS_WAITER_DONE); + } + + System.out.println("Executed " + count + " loops in " + timeMax + + " seconds."); + + return 0; + } +} + + + + diff --git a/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWait3.java b/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWait3.java new file mode 100644 index 00000000000..079876fda2f --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWait3.java @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4413752 8262881 + * @summary Test SuspendThread with ObjectMonitor wait. + * @requires vm.jvmti + * @library /test/lib + * @compile SuspendWithObjectMonitorWait3.java + * @run main/othervm/native -agentlib:SuspendWithObjectMonitorWait SuspendWithObjectMonitorWait3 + */ + +import java.io.PrintStream; + +// +// doWork3 algorithm: +// +// main waiter resumer +// =================== ====================== =================== +// launch waiter +// waiter running +// launch resumer enter threadLock +// while !READY_TO_NOTIFY resumer running +// : threadLock.wait(1) wait for notify +// enter threadLock : : +// set READY_TO_NOTIFY : +// threadLock.notify wait finishes : +// : delay 200ms reenter blocks : +// suspend waiter : +// : : +// : : : +// notify resumer : wait finishes +// delay 1-second : : +// exit threadLock : : +// join resumer : enter threadLock +// : resume waiter +// : : exit threadLock +// : reenter threadLock : +// : resumer exits +// join waiter : +// waiter exits +// +// Note: The sleep(1-second) in main along with the delayed exit +// of threadLock in main forces the resumer thread to reach +// "enter threadLock" and block. This difference from doWork1 +// forces the resumer thread to be contending for threadLock +// while the waiter thread is in the threadLock.wait(1) tight +// loop increasing stress on the monitor sub-system. +// +// Note: sleep(200ms) here while holding the threadLock to allow the +// waiter thread's timed wait to finish before we attempt to +// suspend the waiter thread. +// + +public class SuspendWithObjectMonitorWait3 extends SuspendWithObjectMonitorWaitBase { + + public static void main(String[] args) throws Exception { + if (args.length > 2) { + System.err.println("Invalid number of arguments, there are too many arguments."); + usage(); + } + + try { + System.loadLibrary(AGENT_LIB); + log("Loaded library: " + AGENT_LIB); + } catch (UnsatisfiedLinkError ule) { + log("Failed to load library: " + AGENT_LIB); + log("java.library.path: " + System.getProperty("java.library.path")); + throw ule; + } + + int timeMax = 0; + for (int argIndex = 0; argIndex < args.length; argIndex++) { + if ("-p".equals(args[argIndex])) { + // Handle optional -p arg regardless of position. + printDebug = true; + continue; + } + + if (argIndex < args.length) { + // timeMax is an optional arg. + try { + timeMax = Integer.parseUnsignedInt(args[argIndex]); + } catch (NumberFormatException nfe) { + System.err.println("'" + args[argIndex] + + "': invalid time_max value."); + usage(); + } + } else { + timeMax = DEF_TIME_MAX; + } + } + + System.exit(run(timeMax, System.out) + exit_delta); + } + + public static int run(int timeMax, PrintStream out) { + return (new SuspendWithObjectMonitorWait3()).doWork3(timeMax, out); + } + + // Suspend on the re-entry path of wait. + public int doWork3(int timeMax, PrintStream out) { + SuspendWithObjectMonitorWaitWorker waiter; // waiter thread + SuspendWithObjectMonitorWaitWorker resumer; // resumer thread + + System.out.println("About to execute for " + timeMax + " seconds."); + + long start_time = System.currentTimeMillis(); + while (System.currentTimeMillis() < start_time + (timeMax * 1000)) { + count++; + testState = TS_INIT; // starting the test loop + + // launch the waiter thread + synchronized (barrierLaunch) { + waiter = new SuspendWithObjectMonitorWaitWorker("waiter", 100); + waiter.start(); + + while (testState != TS_WAITER_RUNNING) { + try { + barrierLaunch.wait(0); // wait until it is running + } catch (InterruptedException ex) { + } + } + } + + // launch the resumer thread + synchronized (barrierLaunch) { + resumer = new SuspendWithObjectMonitorWaitWorker("resumer", waiter); + resumer.start(); + + while (testState != TS_RESUMER_RUNNING) { + try { + barrierLaunch.wait(0); // wait until it is running + } catch (InterruptedException ex) { + } + } + } + + checkTestState(TS_RESUMER_RUNNING); + + // The waiter thread was synchronized on threadLock before it + // set TS_WAITER_RUNNING and notified barrierLaunch above so + // we cannot enter threadLock until the waiter thread calls + // threadLock.wait(). + synchronized (threadLock) { + // notify waiter thread so it can try to reenter threadLock + testState = TS_READY_TO_NOTIFY; + threadLock.notify(); + + try { + Thread.sleep(200); + } catch (Exception e) {} + + // wait for the waiter thread to block + logDebug("before contended enter wait"); + int retCode = wait4ContendedEnter(waiter); + if (retCode != 0) { + throw new RuntimeException("error in JVMTI GetThreadState: " + + "retCode=" + retCode); + } + logDebug("done contended enter wait"); + + checkTestState(TS_READY_TO_NOTIFY); + testState = TS_CALL_SUSPEND; + logDebug("before suspend thread"); + retCode = suspendThread(waiter); + if (retCode != 0) { + throw new RuntimeException("error in JVMTI SuspendThread: " + + "retCode=" + retCode); + } + logDebug("suspended thread"); + + // + // At this point, all of the child threads are running + // and we can get to meat of the test: + // + // - suspended threadLock waiter (trying to reenter) + // - a blocked threadLock enter in the resumer thread while the + // threadLock is held by the main thread. + // - resumption of the waiter thread + // - a threadLock enter in the freshly resumed waiter thread + // + + synchronized (barrierResumer) { + checkTestState(TS_CALL_SUSPEND); + + // tell resumer thread to resume waiter thread + testState = TS_READY_TO_RESUME; + barrierResumer.notify(); + + // Can't call checkTestState() here because the + // resumer thread may have already resumed the + // waiter thread. + } + try { + // Delay for 1-second while holding the threadLock to force the + // resumer thread to block on entering the threadLock. + Thread.sleep(1000); + } catch (Exception e) {} + } + + try { + resumer.join(JOIN_MAX * 1000); + if (resumer.isAlive()) { + System.err.println("Failure at " + count + " loops."); + throw new InternalError("resumer thread is stuck"); + } + waiter.join(JOIN_MAX * 1000); + if (waiter.isAlive()) { + System.err.println("Failure at " + count + " loops."); + throw new InternalError("waiter thread is stuck"); + } + } catch (InterruptedException ex) { + } + + checkTestState(TS_WAITER_DONE); + } + + System.out.println("Executed " + count + " loops in " + timeMax + + " seconds."); + + return 0; + } +} + + + + diff --git a/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWaitBase.java b/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWaitBase.java new file mode 100644 index 00000000000..afff7a4de8a --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWaitBase.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +public class SuspendWithObjectMonitorWaitBase { + protected static final String AGENT_LIB = "SuspendWithObjectMonitorWait"; + protected static final int exit_delta = 95; + + protected static final int DEF_TIME_MAX = 60; // default max # secs to test + protected static final int JOIN_MAX = 30; // max # secs to wait for join + + public static final int TS_INIT = 1; // initial testState + public static final int TS_WAITER_RUNNING = 2; // waiter is running + public static final int TS_RESUMER_RUNNING = 3; // resumer is running + public static final int TS_READY_TO_NOTIFY = 4; // ready to notify threadLock + public static final int TS_CALL_SUSPEND = 5; // call suspend on contender + public static final int TS_READY_TO_RESUME = 6; // ready to resume waiter + public static final int TS_CALL_RESUME = 7; // call resume on waiter + public static final int TS_WAITER_DONE = 8; // waiter has run; done + + public static Object barrierLaunch = new Object(); // controls thread launch + public static Object barrierResumer = new Object(); // controls resumer + public static Object threadLock = new Object(); // testing object + + public static long count = 0; + public static boolean printDebug = false; + public volatile static int testState; + + protected static void log(String msg) { System.out.println(msg); } + + native static int suspendThread(SuspendWithObjectMonitorWaitWorker thr); + native static int wait4ContendedEnter(SuspendWithObjectMonitorWaitWorker thr); + + public static void logDebug(String mesg) { + if (printDebug) { + System.err.println(Thread.currentThread().getName() + ": " + mesg); + } + } + + public static void usage() { + System.err.println("Usage: " + AGENT_LIB + " test_case [-p] [time_max]"); + System.err.println("where:"); + System.err.println(" test_case ::= 1 | 2 | 3"); + System.err.println(" -p ::= print debug info"); + System.err.println(" time_max ::= max looping time in seconds"); + System.err.println(" (default is " + DEF_TIME_MAX + + " seconds)"); + System.exit(1); + } + + public static void checkTestState(int exp) { + if (testState != exp) { + System.err.println("Failure at " + count + " loops."); + throw new InternalError("Unexpected test state value: " + + "expected=" + exp + " actual=" + testState); + } + } +} diff --git a/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWaitWorker.java b/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWaitWorker.java new file mode 100644 index 00000000000..4e0361dd099 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWaitWorker.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +class SuspendWithObjectMonitorWaitWorker extends Thread { + private SuspendWithObjectMonitorWaitWorker target; // target for resume operation + private final long waitTimeout; + + public SuspendWithObjectMonitorWaitWorker(String name) { + super(name); + this.waitTimeout = 0; + } + + public SuspendWithObjectMonitorWaitWorker(String name, long waitTimeout) { + super(name); + this.waitTimeout = waitTimeout; + } + + public SuspendWithObjectMonitorWaitWorker(String name, SuspendWithObjectMonitorWaitWorker target) { + super(name); + this.target = target; + this.waitTimeout = 0; + } + + native static int resumeThread(SuspendWithObjectMonitorWaitWorker thr); + + public void run() { + SuspendWithObjectMonitorWait1.logDebug("thread running"); + + // + // Launch the waiter thread: + // - grab the threadLock + // - threadLock.wait() + // - releases threadLock + // + if (getName().equals("waiter")) { + // grab threadLock before we tell main we are running + SuspendWithObjectMonitorWait1.logDebug("before enter threadLock"); + synchronized(SuspendWithObjectMonitorWait1.threadLock) { + SuspendWithObjectMonitorWait1.logDebug("enter threadLock"); + + SuspendWithObjectMonitorWait1.checkTestState(SuspendWithObjectMonitorWait1.TS_INIT); + + synchronized(SuspendWithObjectMonitorWait1.barrierLaunch) { + // tell main we are running + SuspendWithObjectMonitorWait1.testState = SuspendWithObjectMonitorWait1.TS_WAITER_RUNNING; + SuspendWithObjectMonitorWait1.barrierLaunch.notify(); + } + + SuspendWithObjectMonitorWait1.logDebug("before wait"); + + // TS_READY_TO_NOTIFY is set after the main thread has + // entered threadLock so a spurious wakeup can't get the + // waiter thread out of this threadLock.wait(0) call: + while (SuspendWithObjectMonitorWait1.testState <= SuspendWithObjectMonitorWait1.TS_READY_TO_NOTIFY) { + try { + SuspendWithObjectMonitorWait1.threadLock.wait(waitTimeout); + } catch (InterruptedException ex) { + } + } + + SuspendWithObjectMonitorWait1.logDebug("after wait"); + + SuspendWithObjectMonitorWait1.checkTestState(SuspendWithObjectMonitorWait1.TS_CALL_RESUME); + SuspendWithObjectMonitorWait1.testState = SuspendWithObjectMonitorWait1.TS_WAITER_DONE; + + SuspendWithObjectMonitorWait1.logDebug("exit threadLock"); + } + } + // + // Launch the resumer thread: + // - tries to grab the threadLock (should not block with doWork1!) + // - grabs threadLock + // - resumes the waiter thread + // - releases threadLock + // + else if (getName().equals("resumer")) { + synchronized(SuspendWithObjectMonitorWait1.barrierResumer) { + synchronized(SuspendWithObjectMonitorWait1.barrierLaunch) { + // tell main we are running + SuspendWithObjectMonitorWait1.testState = SuspendWithObjectMonitorWait1.TS_RESUMER_RUNNING; + SuspendWithObjectMonitorWait1.barrierLaunch.notify(); + } + SuspendWithObjectMonitorWait1.logDebug("thread waiting"); + while (SuspendWithObjectMonitorWait1.testState != SuspendWithObjectMonitorWait1.TS_READY_TO_RESUME) { + try { + // wait for main to tell us when to continue + SuspendWithObjectMonitorWait1.barrierResumer.wait(0); + } catch (InterruptedException ex) { + } + } + } + + SuspendWithObjectMonitorWait1.logDebug("before enter threadLock"); + synchronized(SuspendWithObjectMonitorWait1.threadLock) { + SuspendWithObjectMonitorWait1.logDebug("enter threadLock"); + + SuspendWithObjectMonitorWait1.checkTestState(SuspendWithObjectMonitorWait1.TS_READY_TO_RESUME); + SuspendWithObjectMonitorWait1.testState = SuspendWithObjectMonitorWait1.TS_CALL_RESUME; + + // resume the waiter thread so waiter.join() can work + SuspendWithObjectMonitorWait1.logDebug("before resume thread"); + int retCode = resumeThread(target); + if (retCode != 0) { + throw new RuntimeException("error in JVMTI ResumeThread: " + + "retCode=" + retCode); + } + SuspendWithObjectMonitorWait1.logDebug("resumed thread"); + + SuspendWithObjectMonitorWait1.logDebug("exit threadLock"); + } + } + } +} diff --git a/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/libSuspendWithObjectMonitorWait.cpp b/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/libSuspendWithObjectMonitorWait.cpp index 3706cba76dc..57ba0be5f1c 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/libSuspendWithObjectMonitorWait.cpp +++ b/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/libSuspendWithObjectMonitorWait.cpp @@ -36,7 +36,7 @@ static jvmtiEnv* jvmti = nullptr; } while (0) JNIEXPORT int JNICALL -Java_SuspendWithObjectMonitorWait_suspendThread(JNIEnv *jni, jclass cls, jthread thr) { +Java_SuspendWithObjectMonitorWaitBase_suspendThread(JNIEnv *jni, jclass cls, jthread thr) { return jvmti->SuspendThread(thr); } @@ -46,7 +46,7 @@ Java_SuspendWithObjectMonitorWaitWorker_resumeThread(JNIEnv *jni, jclass cls, jt } JNIEXPORT jint JNICALL -Java_SuspendWithObjectMonitorWait_wait4ContendedEnter(JNIEnv *jni, jclass cls, jthread thr) { +Java_SuspendWithObjectMonitorWaitBase_wait4ContendedEnter(JNIEnv *jni, jclass cls, jthread thr) { jvmtiError err; jint thread_state; do {