Path: blob/master/test/hotspot/jtreg/serviceability/jvmti/SuspendWithRawMonitorEnter/SuspendWithRawMonitorEnter.java
41153 views
/*1* Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/*24* @test25* @bug 441375226* @summary Test SuspendThread with RawMonitor enter.27* @requires vm.jvmti28* @library /test/lib29* @compile SuspendWithRawMonitorEnter.java30* @run main/othervm/native -agentlib:SuspendWithRawMonitorEnter SuspendWithRawMonitorEnter31*/3233import java.io.PrintStream;3435//36// main blocker contender resumer37// ================= ================ =================== ================38// launch blocker39// <launch returns> blocker running40// launch contender enter threadLock41// <launch returns> wait for notify contender running42// launch resumer : block on threadLock43// <launch returns> : : resumer running44// suspend contender : <suspended> wait for notify45// <ready to test> : : :46// : : : :47// notify blocker wait finishes : :48// notify resumer exit threadLock : wait finishes49// join blocker : : enter threadLock50// <join returns> blocker exits <resumed> resume contender51// join resumer : exit threadLock52// <join returns> enter threadLock resumer exits53// join contender exit threadLock54// <join returns> contender exits55//5657public class SuspendWithRawMonitorEnter {58private static final String AGENT_LIB = "SuspendWithRawMonitorEnter";59private static final int exit_delta = 95;6061private static final int DEF_TIME_MAX = 60; // default max # secs to test62private static final int JOIN_MAX = 30; // max # secs to wait for join6364public static final int TS_INIT = 1; // initial testState65public static final int TS_BLOCKER_RUNNING = 2; // blocker is running66public static final int TS_CONTENDER_RUNNING = 3; // contender is running67public static final int TS_RESUMER_RUNNING = 4; // resumer is running68public static final int TS_CALL_SUSPEND = 5; // call suspend on contender69public static final int TS_DONE_BLOCKING = 6; // done blocking threadLock70public static final int TS_READY_TO_RESUME = 7; // ready to resume contender71public static final int TS_CALL_RESUME = 8; // call resume on contender72public static final int TS_CONTENDER_DONE = 9; // contender has run; done7374public static Object barrierLaunch = new Object(); // controls thread launch75public static Object barrierBlocker = new Object(); // controls blocker76public static Object barrierResumer = new Object(); // controls resumer7778public static long count = 0;79public static boolean printDebug = false;80public volatile static int testState;8182private static void log(String msg) { System.out.println(msg); }8384native static int createRawMonitor();85native static int destroyRawMonitor();86native static int suspendThread(SuspendWithRawMonitorEnterWorker thr);8788public static void main(String[] args) throws Exception {89try {90System.loadLibrary(AGENT_LIB);91log("Loaded library: " + AGENT_LIB);92} catch (UnsatisfiedLinkError ule) {93log("Failed to load library: " + AGENT_LIB);94log("java.library.path: " + System.getProperty("java.library.path"));95throw ule;96}9798int timeMax = 0;99if (args.length == 0) {100timeMax = DEF_TIME_MAX;101} else {102int argIndex = 0;103int argsLeft = args.length;104if (args[0].equals("-p")) {105printDebug = true;106argIndex = 1;107argsLeft--;108}109if (argsLeft == 0) {110timeMax = DEF_TIME_MAX;111} else if (argsLeft == 1) {112try {113timeMax = Integer.parseUnsignedInt(args[argIndex]);114} catch (NumberFormatException nfe) {115System.err.println("'" + args[argIndex] +116"': invalid timeMax value.");117usage();118}119} else {120usage();121}122}123124System.exit(run(timeMax, System.out) + exit_delta);125}126127public static void logDebug(String mesg) {128if (printDebug) {129System.err.println(Thread.currentThread().getName() + ": " + mesg);130}131}132133public static void usage() {134System.err.println("Usage: " + AGENT_LIB + " [-p][time_max]");135System.err.println("where:");136System.err.println(" -p ::= print debug info");137System.err.println(" time_max ::= max looping time in seconds");138System.err.println(" (default is " + DEF_TIME_MAX +139" seconds)");140System.exit(1);141}142143public static int run(int timeMax, PrintStream out) {144return (new SuspendWithRawMonitorEnter()).doWork(timeMax, out);145}146147public static void checkTestState(int exp) {148if (testState != exp) {149System.err.println("Failure at " + count + " loops.");150throw new InternalError("Unexpected test state value: "151+ "expected=" + exp + " actual=" + testState);152}153}154155public int doWork(int timeMax, PrintStream out) {156SuspendWithRawMonitorEnterWorker blocker; // blocker thread157SuspendWithRawMonitorEnterWorker contender; // contender thread158SuspendWithRawMonitorEnterWorker resumer; // resumer thread159160int retCode = createRawMonitor();161if (retCode != 0) {162throw new RuntimeException("error in JVMTI CreateRawMonitor: " +163"retCode=" + retCode);164}165logDebug("created threadLock");166167System.out.println("About to execute for " + timeMax + " seconds.");168169long start_time = System.currentTimeMillis();170while (System.currentTimeMillis() < start_time + (timeMax * 1000)) {171count++;172testState = TS_INIT; // starting the test loop173174// launch the blocker thread175synchronized (barrierLaunch) {176blocker = new SuspendWithRawMonitorEnterWorker("blocker");177blocker.start();178179while (testState != TS_BLOCKER_RUNNING) {180try {181barrierLaunch.wait(0); // wait until it is running182} catch (InterruptedException ex) {183}184}185}186187// launch the contender thread188synchronized (barrierLaunch) {189contender = new SuspendWithRawMonitorEnterWorker("contender");190contender.start();191192while (testState != TS_CONTENDER_RUNNING) {193try {194barrierLaunch.wait(0); // wait until it is running195} catch (InterruptedException ex) {196}197}198}199200// launch the resumer thread201synchronized (barrierLaunch) {202resumer = new SuspendWithRawMonitorEnterWorker("resumer", contender);203resumer.start();204205while (testState != TS_RESUMER_RUNNING) {206try {207barrierLaunch.wait(0); // wait until it is running208} catch (InterruptedException ex) {209}210}211}212213//214// Known bug: We don't have a way of knowing when the215// contender thread contends on the threadLock. If we216// suspend it before it has blocked, then we don't really217// have contention. However, the resumer thread won't218// resume the contender thread until after it has grabbed219// the threadLock so we don't have a lock order problem220// and the test won't fall over.221//222// We reduce the size of this timing window by launching223// the resumer thread after the contender thread. So the224// contender thread has all the setup time for the resumer225// thread to call JVM/TI RawMonitorEnter() and block on226// the threadLock.227//228checkTestState(TS_RESUMER_RUNNING);229testState = TS_CALL_SUSPEND;230logDebug("before suspend thread");231retCode = suspendThread(contender);232if (retCode != 0) {233throw new RuntimeException("error in JVMTI SuspendThread: " +234"retCode=" + retCode);235}236logDebug("suspended thread");237238//239// At this point, all of the child threads are running240// and we can get to meat of the test:241//242// - suspended threadLock contender243// - a threadLock exit in the blocker thread244// - a threadLock enter in the resumer thread245// - resumption of the contender thread246// - a threadLock enter in the freshly resumed contender thread247//248synchronized (barrierBlocker) {249checkTestState(TS_CALL_SUSPEND);250251// tell blocker thread to exit threadLock252testState = TS_DONE_BLOCKING;253barrierBlocker.notify();254}255256synchronized (barrierResumer) {257// tell resumer thread to resume contender thread258testState = TS_READY_TO_RESUME;259barrierResumer.notify();260261// Can't call checkTestState() here because the262// resumer thread may have already resumed the263// contender thread.264}265266try {267blocker.join();268resumer.join(JOIN_MAX * 1000);269if (resumer.isAlive()) {270System.err.println("Failure at " + count + " loops.");271throw new InternalError("resumer thread is stuck");272}273contender.join(JOIN_MAX * 1000);274if (contender.isAlive()) {275System.err.println("Failure at " + count + " loops.");276throw new InternalError("contender thread is stuck");277}278} catch (InterruptedException ex) {279}280281checkTestState(TS_CONTENDER_DONE);282}283retCode = destroyRawMonitor();284if (retCode != 0) {285throw new RuntimeException("error in JVMTI DestroyRawMonitor: " +286"retCode=" + retCode);287}288logDebug("destroyed threadLock");289290System.out.println("Executed " + count + " loops in " + timeMax +291" seconds.");292293return 0;294}295}296297class SuspendWithRawMonitorEnterWorker extends Thread {298private SuspendWithRawMonitorEnterWorker target; // target for resume operation299300public SuspendWithRawMonitorEnterWorker(String name) {301super(name);302}303304public SuspendWithRawMonitorEnterWorker(String name, SuspendWithRawMonitorEnterWorker target) {305super(name);306this.target = target;307}308309native static int rawMonitorEnter();310native static int rawMonitorExit();311native static int resumeThread(SuspendWithRawMonitorEnterWorker thr);312313public void run() {314SuspendWithRawMonitorEnter.logDebug("thread running");315316//317// Launch the blocker thread:318// - grabs threadLock319// - holds threadLock until we tell it let go320// - releases threadLock321//322int retCode;323if (getName().equals("blocker")) {324// grab threadLock before we tell main we are running325SuspendWithRawMonitorEnter.logDebug("before enter threadLock");326retCode = rawMonitorEnter();327if (retCode != 0) {328throw new RuntimeException("error in JVMTI RawMonitorEnter: " +329"retCode=" + retCode);330}331SuspendWithRawMonitorEnter.logDebug("enter threadLock");332333SuspendWithRawMonitorEnter.checkTestState(SuspendWithRawMonitorEnter.TS_INIT);334335// recursive entry336SuspendWithRawMonitorEnter.logDebug("before recursive enter threadLock");337retCode = rawMonitorEnter();338if (retCode != 0) {339throw new RuntimeException("error in JVMTI RawMonitorEnter: " +340"retCode=" + retCode);341}342SuspendWithRawMonitorEnter.logDebug("recursive enter threadLock");343344SuspendWithRawMonitorEnter.logDebug("before recursive exit threadLock");345retCode = rawMonitorExit();346if (retCode != 0) {347throw new RuntimeException("error in JVMTI RawMonitorExit: " +348"retCode=" + retCode);349}350SuspendWithRawMonitorEnter.logDebug("recursive exit threadLock");351352synchronized(SuspendWithRawMonitorEnter.barrierBlocker) {353synchronized(SuspendWithRawMonitorEnter.barrierLaunch) {354// tell main we are running355SuspendWithRawMonitorEnter.testState = SuspendWithRawMonitorEnter.TS_BLOCKER_RUNNING;356SuspendWithRawMonitorEnter.barrierLaunch.notify();357}358SuspendWithRawMonitorEnter.logDebug("thread waiting");359// TS_READY_TO_RESUME is set right after TS_DONE_BLOCKING360// is set so either can get the blocker thread out of361// this wait() wrapper:362while (SuspendWithRawMonitorEnter.testState != SuspendWithRawMonitorEnter.TS_DONE_BLOCKING &&363SuspendWithRawMonitorEnter.testState != SuspendWithRawMonitorEnter.TS_READY_TO_RESUME) {364try {365// wait for main to tell us when to exit threadLock366SuspendWithRawMonitorEnter.barrierBlocker.wait(0);367} catch (InterruptedException ex) {368}369}370SuspendWithRawMonitorEnter.logDebug("before exit threadLock");371retCode = rawMonitorExit();372if (retCode != 0) {373throw new RuntimeException("error in JVMTI RawMonitorExit: "374+ "retCode=" + retCode);375}376SuspendWithRawMonitorEnter.logDebug("exit threadLock");377}378}379//380// Launch the contender thread:381// - tries to grab the threadLock382// - grabs threadLock383// - releases threadLock384//385else if (getName().equals("contender")) {386synchronized(SuspendWithRawMonitorEnter.barrierLaunch) {387// tell main we are running388SuspendWithRawMonitorEnter.testState = SuspendWithRawMonitorEnter.TS_CONTENDER_RUNNING;389SuspendWithRawMonitorEnter.barrierLaunch.notify();390}391392SuspendWithRawMonitorEnter.logDebug("before enter threadLock");393retCode = rawMonitorEnter();394if (retCode != 0) {395throw new RuntimeException("error in JVMTI RawMonitorEnter: " +396"retCode=" + retCode);397}398SuspendWithRawMonitorEnter.logDebug("enter threadLock");399400SuspendWithRawMonitorEnter.checkTestState(SuspendWithRawMonitorEnter.TS_CALL_RESUME);401SuspendWithRawMonitorEnter.testState = SuspendWithRawMonitorEnter.TS_CONTENDER_DONE;402403SuspendWithRawMonitorEnter.logDebug("before exit threadLock");404retCode = rawMonitorExit();405if (retCode != 0) {406throw new RuntimeException("error in JVMTI RawMonitorExit: " +407"retCode=" + retCode);408}409SuspendWithRawMonitorEnter.logDebug("exit threadLock");410}411//412// Launch the resumer thread:413// - tries to grab the threadLock (should not block!)414// - grabs threadLock415// - resumes the contended thread416// - releases threadLock417//418else if (getName().equals("resumer")) {419synchronized(SuspendWithRawMonitorEnter.barrierResumer) {420synchronized(SuspendWithRawMonitorEnter.barrierLaunch) {421// tell main we are running422SuspendWithRawMonitorEnter.testState = SuspendWithRawMonitorEnter.TS_RESUMER_RUNNING;423SuspendWithRawMonitorEnter.barrierLaunch.notify();424}425SuspendWithRawMonitorEnter.logDebug("thread waiting");426while (SuspendWithRawMonitorEnter.testState != SuspendWithRawMonitorEnter.TS_READY_TO_RESUME) {427try {428// wait for main to tell us when to continue429SuspendWithRawMonitorEnter.barrierResumer.wait(0);430} catch (InterruptedException ex) {431}432}433}434SuspendWithRawMonitorEnter.logDebug("before enter threadLock");435retCode = rawMonitorEnter();436if (retCode != 0) {437throw new RuntimeException("error in JVMTI RawMonitorEnter: " +438"retCode=" + retCode);439}440SuspendWithRawMonitorEnter.logDebug("enter threadLock");441442SuspendWithRawMonitorEnter.checkTestState(SuspendWithRawMonitorEnter.TS_READY_TO_RESUME);443SuspendWithRawMonitorEnter.testState = SuspendWithRawMonitorEnter.TS_CALL_RESUME;444445// resume the contender thread so contender.join() can work446SuspendWithRawMonitorEnter.logDebug("before resume thread");447retCode = resumeThread(target);448if (retCode != 0) {449throw new RuntimeException("error in JVMTI ResumeThread: " +450"retCode=" + retCode);451}452SuspendWithRawMonitorEnter.logDebug("resumed thread");453454SuspendWithRawMonitorEnter.logDebug("before exit threadLock");455retCode = rawMonitorExit();456if (retCode != 0) {457throw new RuntimeException("error in JVMTI RawMonitorExit: " +458"retCode=" + retCode);459}460SuspendWithRawMonitorEnter.logDebug("exit threadLock");461}462}463}464465466