1 /* 2 * Copyright (c) 2023, 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 id=default 26 * @summary Verifies JVMTI StopThread support for virtual threads. 27 * @requires vm.continuations 28 * @run main/othervm/native -agentlib:StopThreadTest StopThreadTest 29 */ 30 31 /* 32 * @test id=no-vmcontinuations 33 * @summary Verifies JVMTI StopThread support for bound virtual threads. 34 * @run main/othervm/native -agentlib:StopThreadTest -XX:+UnlockExperimentalVMOptions -XX:-VMContinuations StopThreadTest 35 */ 36 37 /* 38 * @test id=platform 39 * @summary Verifies JVMTI StopThread support for platform threads. 40 * @run main/othervm/native -agentlib:StopThreadTest StopThreadTest platform 41 */ 42 43 import java.lang.AssertionError; 44 45 /* 46 * The test exercises the JVMTI function: StopThread(jthread). 47 * The test creates a new virtual or platform thread. 48 * Its method run() invokes the following methods: 49 * - method A() that is blocked on a monitor 50 * - method B() that is stopped at a breakpoint 51 * - method C() that forces agent to send AssertionError exception to its own thread 52 * All cases are using JVMTI StopThread to send an AssertionError object. 53 */ 54 public class StopThreadTest { 55 private static final String agentLib = "StopThreadTest"; 56 static final int JVMTI_ERROR_NONE = 0; 57 static final int THREAD_NOT_SUSPENDED = 13; 58 static final int PASSED = 0; 59 static final int FAILED = 2; 60 61 static void log(String str) { System.out.println(str); } 62 63 static native void prepareAgent(Class taskClass, Object exceptionObject); 64 static native void suspendThread(Thread thread); 65 static native void resumeThread(Thread thread); 66 static native void ensureAtBreakpoint(); 67 static native void notifyAtBreakpoint(); 68 static native int stopThread(Thread thread); 69 70 static int status = PASSED; 71 static boolean is_virtual = true; 72 73 static void setFailed(String msg) { 74 log("\nFAILED: " + msg); 75 status = FAILED; 76 } 77 78 static void throwFailed(String msg) { 79 log("\nFAILED: " + msg); 80 throw new RuntimeException("StopThreadTest failed!"); 81 } 82 83 public static void main(String args[]) { 84 is_virtual = !(args.length > 0 && args[0].equals("platform")); 85 run(); 86 if (status == FAILED) { 87 throwFailed("StopThreadTest!"); 88 } 89 log("\nStopThreadTest passed"); 90 } 91 92 public static void run() { 93 TestTask testTask = new TestTask(); 94 Thread testTaskThread = null; 95 AssertionError excObject = new AssertionError(); 96 int retCode; 97 98 prepareAgent(TestTask.class, excObject); 99 100 log("\nMain #A: method A() must be blocked on entering a synchronized statement"); 101 synchronized (TestTask.lock) { 102 if (is_virtual) { 103 testTaskThread = Thread.ofVirtual().name("TestTaskThread").start(testTask); 104 } else { 105 testTaskThread = Thread.ofPlatform().name("TestTaskThread").start(testTask); 106 } 107 TestTask.ensureAtPointA(); 108 109 if (is_virtual) { // this check is for virtual target thread only 110 log("\nMain #A.1: unsuspended"); 111 retCode = stopThread(testTaskThread); 112 if (retCode != THREAD_NOT_SUSPENDED) { 113 throwFailed("Main #A.1: expected THREAD_NOT_SUSPENDED instead of: " + retCode); 114 } else { 115 log("Main #A.1: got expected THREAD_NOT_SUSPENDED"); 116 } 117 } 118 119 log("\nMain #A.2: suspended"); 120 suspendThread(testTaskThread); 121 retCode = stopThread(testTaskThread); 122 if (retCode != JVMTI_ERROR_NONE) { 123 throwFailed("Main #A.2: expected JVMTI_ERROR_NONE instead of: " + retCode); 124 } else { 125 log("Main #A.2: got expected JVMTI_ERROR_NONE"); 126 } 127 resumeThread(testTaskThread); 128 } 129 log("\nMain #B: method B() must be blocked in a breakpoint event handler"); 130 { 131 ensureAtBreakpoint(); 132 133 if (is_virtual) { // this check is for virtual target thread only 134 log("\nMain #B.1: unsuspended"); 135 retCode = stopThread(testTaskThread); 136 if (retCode != THREAD_NOT_SUSPENDED) { 137 throwFailed("Main #B.1: expected THREAD_NOT_SUSPENDED instead of: " + retCode); 138 } 139 } 140 141 log("\nMain #B.2: suspended"); 142 suspendThread(testTaskThread); 143 retCode = stopThread(testTaskThread); 144 if (retCode != JVMTI_ERROR_NONE) { 145 throwFailed("Main #B.2: expected JVMTI_ERROR_NONE"); 146 } 147 resumeThread(testTaskThread); 148 149 notifyAtBreakpoint(); 150 } 151 152 log("\nMain #C: method C() sends AssertionError object to its own thread"); 153 { 154 // StopThread is called from the test task (own thread) and expected to succeed. 155 // No suspension of the test task thread is required or can be done in this case. 156 TestTask.ensureFinished(); 157 } 158 159 try { 160 testTaskThread.join(); 161 } catch (InterruptedException ex) { 162 throwFailed("Unexpected " + ex); 163 } 164 } 165 166 167 static class TestTask implements Runnable { 168 static Object lock = new Object(); 169 static void log(String str) { System.out.println(str); } 170 171 static volatile boolean atPointA = false; 172 static volatile boolean finished = false; 173 174 static void sleep(long millis) { 175 try { 176 Thread.sleep(millis); 177 } catch (InterruptedException e) { 178 throw new RuntimeException("Interruption in TestTask.sleep: \n\t" + e); 179 } 180 } 181 182 static void ensureAtPointA() { 183 while (!atPointA) { 184 sleep(1); 185 } 186 } 187 188 // Ensure thread is finished. 189 static void ensureFinished() { 190 while (!finished) { 191 sleep(1); 192 } 193 } 194 195 public void run() { 196 log("TestTask.run: started"); 197 198 boolean seenExceptionFromA = false; 199 try { 200 A(); 201 } catch (AssertionError ex) { 202 log("TestTask.run: caught expected AssertionError from method A()"); 203 seenExceptionFromA = true; 204 } 205 Thread.interrupted(); 206 if (!seenExceptionFromA) { 207 StopThreadTest.setFailed("TestTask.run: expected AssertionError from method A()"); 208 } 209 sleep(1); // to cause yield 210 211 boolean seenExceptionFromB = false; 212 try { 213 B(); 214 } catch (AssertionError ex) { 215 log("TestTask.run: caught expected AssertionError from method B()"); 216 seenExceptionFromB = true; 217 } 218 Thread.interrupted(); 219 if (!seenExceptionFromB) { 220 StopThreadTest.setFailed("TestTask.run: expected AssertionError from method B()"); 221 } 222 sleep(1); // to cause yield 223 224 boolean seenExceptionFromC = false; 225 try { 226 C(); 227 } catch (AssertionError ex) { 228 log("TestTask.run: caught expected AssertionError from method C()"); 229 seenExceptionFromC = true; 230 } 231 Thread.interrupted(); 232 if (!seenExceptionFromC) { 233 StopThreadTest.setFailed("TestTask.run: expected AssertionError from method C()"); 234 } 235 finished = true; 236 } 237 238 // Method is blocked on entering a synchronized statement. 239 // StopThread is used to send an AssertionError object two times: 240 // - when not suspended: THREAD_NOT_SUSPENDED is expected 241 // - when suspended: JVMTI_ERROR_NONE is expected 242 static void A() { 243 log("TestTask.A: started"); 244 atPointA = true; 245 synchronized (lock) { 246 } 247 log("TestTask.A: finished"); 248 } 249 250 // A breakpoint is set at start of this method. 251 // StopThread is used to send an AssertionError object two times: 252 // - when not suspended: THREAD_NOT_SUSPENDED is expected 253 // - when suspended: expected to succeed 254 static void B() { 255 log("TestTask.B: started"); 256 } 257 258 // This method uses StopThread to send an AssertionError object to 259 // its own thread. It is expected to succeed. 260 static void C() { 261 log("TestTask.C: started"); 262 StopThreadTest.stopThread(Thread.currentThread()); 263 log("TestTask.C: finished"); 264 } 265 } 266 }