1 /* 2 * Copyright (c) 2025, 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 * @modules java.base/java.lang:+open jdk.management 27 * @library /test/lib 28 * @requires vm.continuations & vm.opt.LockingMode != 1 29 @run junit/othervm/timeout=480 -XX:CompileCommand=exclude,KlassInit::lambda$testReleaseAtKlassInit* -XX:CompileCommand=exclude,KlassInit$$Lambda*::run -XX:CompileCommand=exclude,KlassInit$1Driver::foo KlassInit 30 */ 31 32 /* 33 * @test id=Xint 34 * @modules java.base/java.lang:+open jdk.management 35 * @library /test/lib 36 * @requires vm.continuations & vm.opt.LockingMode != 1 37 @run junit/othervm/timeout=480 -Xint -XX:CompileCommand=exclude,KlassInit::lambda$testReleaseAtKlassInit* -XX:CompileCommand=exclude,KlassInit$$Lambda*::run -XX:CompileCommand=exclude,KlassInit$1Driver::foo KlassInit 38 */ 39 40 /* 41 * @test id=Xcomp 42 * @modules java.base/java.lang:+open jdk.management 43 * @library /test/lib 44 * @requires vm.continuations & vm.opt.LockingMode != 1 45 @run junit/othervm/timeout=480 -Xcomp -XX:CompileCommand=exclude,KlassInit::lambda$testReleaseAtKlassInit* -XX:CompileCommand=exclude,KlassInit$$Lambda*::run -XX:CompileCommand=exclude,KlassInit$1Driver::foo KlassInit 46 */ 47 48 /* 49 * @test id=Xcomp-TieredStopAtLevel1 50 * @modules java.base/java.lang:+open jdk.management 51 * @library /test/lib 52 * @requires vm.continuations & vm.opt.LockingMode != 1 53 @run junit/othervm/timeout=480 -Xcomp -XX:TieredStopAtLevel=1 -XX:CompileCommand=exclude,KlassInit::lambda$testReleaseAtKlassInit* -XX:CompileCommand=exclude,KlassInit$$Lambda*::run -XX:CompileCommand=exclude,KlassInit$1Driver::foo KlassInit 54 */ 55 56 /* 57 * @test id=Xcomp-noTieredCompilation 58 * @modules java.base/java.lang:+open jdk.management 59 * @library /test/lib 60 * @requires vm.continuations & vm.opt.LockingMode != 1 61 @run junit/othervm/timeout=480 -Xcomp -XX:-TieredCompilation -XX:CompileCommand=exclude,KlassInit::lambda$testReleaseAtKlassInit* -XX:CompileCommand=exclude,KlassInit$$Lambda*::run -XX:CompileCommand=exclude,KlassInit$1Driver::foo KlassInit 62 */ 63 64 /* 65 * @test id=gc 66 * @modules java.base/java.lang:+open jdk.management 67 * @library /test/lib 68 * @requires vm.debug == true & vm.continuations & vm.opt.LockingMode != 1 69 @run junit/othervm/timeout=480 -XX:+UnlockDiagnosticVMOptions -XX:+FullGCALot -XX:FullGCALotInterval=1000 -XX:CompileCommand=exclude,KlassInit::lambda$testReleaseAtKlassInit* -XX:CompileCommand=exclude,KlassInit$$Lambda*::run -XX:CompileCommand=exclude,KlassInit$1Driver::foo KlassInit 70 */ 71 72 import java.util.ArrayList; 73 import java.util.List; 74 import java.util.Set; 75 import java.util.concurrent.atomic.AtomicInteger; 76 import java.util.concurrent.atomic.AtomicBoolean; 77 import java.util.concurrent.CountDownLatch; 78 import java.util.concurrent.locks.LockSupport; 79 80 import java.util.stream.Stream; 81 import java.util.stream.Collectors; 82 83 import org.junit.jupiter.api.Test; 84 import static org.junit.jupiter.api.Assertions.*; 85 import org.junit.jupiter.params.ParameterizedTest; 86 import org.junit.jupiter.params.provider.MethodSource; 87 import org.junit.jupiter.params.provider.Arguments; 88 89 class KlassInit { 90 static final int MAX_VTHREAD_COUNT = 8 * Runtime.getRuntime().availableProcessors(); 91 static CountDownLatch finishInvokeStatic1 = new CountDownLatch(1); 92 static CountDownLatch finishInvokeStatic2 = new CountDownLatch(1); 93 static CountDownLatch finishInvokeStatic3 = new CountDownLatch(1); 94 static CountDownLatch finishNew = new CountDownLatch(1); 95 static CountDownLatch finishGetStatic = new CountDownLatch(1); 96 static CountDownLatch finishPutStatic = new CountDownLatch(1); 97 static CountDownLatch finishFailedInit = new CountDownLatch(1); 98 99 /** 100 * Test that threads blocked waiting for klass to be initialized 101 * on invokestatic bytecode release the carrier. 102 */ 103 @Test 104 void testReleaseAtKlassInitInvokeStatic1() throws Exception { 105 class TestClass { 106 static { 107 try { 108 finishInvokeStatic1.await(); 109 } catch(InterruptedException e) {} 110 } 111 static void m() { 112 } 113 } 114 115 Thread[] vthreads = new Thread[MAX_VTHREAD_COUNT]; 116 CountDownLatch[] started = new CountDownLatch[MAX_VTHREAD_COUNT]; 117 for (int i = 0; i < MAX_VTHREAD_COUNT; i++) { 118 final int id = i; 119 started[i] = new CountDownLatch(1); 120 vthreads[i] = Thread.ofVirtual().start(() -> { 121 started[id].countDown(); 122 TestClass.m(); 123 }); 124 } 125 for (int i = 0; i < MAX_VTHREAD_COUNT; i++) { 126 started[i].await(); 127 await(vthreads[i], Thread.State.WAITING); 128 } 129 130 finishInvokeStatic1.countDown(); 131 for (int i = 0; i < MAX_VTHREAD_COUNT; i++) { 132 vthreads[i].join(); 133 } 134 } 135 136 /** 137 * Test with static method that takes arguments. 138 */ 139 @Test 140 void testReleaseAtKlassInitInvokeStatic2() throws Exception { 141 class TestClass { 142 static { 143 try { 144 finishInvokeStatic2.await(); 145 } catch(InterruptedException e) {} 146 } 147 static void m(ArrayList<String> list, int id) { 148 String str = list.get(0); 149 if (str != null && str.equals("VThread#" + id)) { 150 list.add("Success"); 151 } 152 } 153 } 154 155 Thread[] vthreads = new Thread[MAX_VTHREAD_COUNT]; 156 CountDownLatch[] started = new CountDownLatch[MAX_VTHREAD_COUNT]; 157 ArrayList<String>[] lists = new ArrayList[MAX_VTHREAD_COUNT]; 158 for (int i = 0; i < MAX_VTHREAD_COUNT; i++) { 159 final int id = i; 160 started[i] = new CountDownLatch(1); 161 lists[i] = new ArrayList<>(List.of("VThread#" + i)); 162 vthreads[i] = Thread.ofVirtual().start(() -> { 163 started[id].countDown(); 164 TestClass.m(lists[id], id); 165 }); 166 } 167 for (int i = 0; i < MAX_VTHREAD_COUNT; i++) { 168 started[i].await(); 169 await(vthreads[i], Thread.State.WAITING); 170 } 171 172 System.gc(); 173 finishInvokeStatic2.countDown(); 174 for (int i = 0; i < MAX_VTHREAD_COUNT; i++) { 175 vthreads[i].join(); 176 assertEquals(lists[i].get(1), "Success"); 177 } 178 } 179 180 /** 181 * Test invokestatic as first bytecode in method. 182 */ 183 @Test 184 void testReleaseAtKlassInitInvokeStatic3() throws Exception { 185 class TestClass { 186 static { 187 try { 188 finishInvokeStatic3.await(); 189 } catch(InterruptedException e) {} 190 } 191 static void m() { 192 } 193 } 194 class Driver { 195 static void foo() { 196 TestClass.m(); 197 } 198 } 199 200 Thread[] vthreads = new Thread[MAX_VTHREAD_COUNT]; 201 CountDownLatch[] started = new CountDownLatch[MAX_VTHREAD_COUNT]; 202 started[0] = new CountDownLatch(1); 203 vthreads[0] = Thread.ofVirtual().start(() -> { 204 started[0].countDown(); 205 TestClass.m(); 206 }); 207 started[0].await(); 208 await(vthreads[0], Thread.State.WAITING); 209 210 for (int i = 1; i < MAX_VTHREAD_COUNT; i++) { 211 final int id = i; 212 started[i] = new CountDownLatch(1); 213 vthreads[i] = Thread.ofVirtual().start(() -> { 214 started[id].countDown(); 215 Driver.foo(); 216 }); 217 } 218 for (int i = 1; i < MAX_VTHREAD_COUNT; i++) { 219 started[i].await(); 220 await(vthreads[i], Thread.State.WAITING); 221 } 222 223 finishInvokeStatic3.countDown(); 224 for (int i = 0; i < MAX_VTHREAD_COUNT; i++) { 225 vthreads[i].join(); 226 } 227 } 228 229 /** 230 * Test that threads blocked waiting for klass to be initialized 231 * on new bytecode release the carrier. 232 */ 233 @Test 234 void testReleaseAtKlassInitNew() throws Exception { 235 class TestClass { 236 static { 237 try { 238 finishNew.await(); 239 } catch(InterruptedException e) {} 240 } 241 void m() { 242 } 243 } 244 245 Thread[] vthreads = new Thread[MAX_VTHREAD_COUNT]; 246 CountDownLatch[] started = new CountDownLatch[MAX_VTHREAD_COUNT]; 247 for (int i = 0; i < MAX_VTHREAD_COUNT; i++) { 248 final int id = i; 249 started[i] = new CountDownLatch(1); 250 vthreads[i] = Thread.ofVirtual().start(() -> { 251 started[id].countDown(); 252 TestClass x = new TestClass(); 253 x.m(); 254 }); 255 } 256 for (int i = 0; i < MAX_VTHREAD_COUNT; i++) { 257 started[i].await(); 258 await(vthreads[i], Thread.State.WAITING); 259 } 260 261 finishNew.countDown(); 262 for (int i = 0; i < MAX_VTHREAD_COUNT; i++) { 263 vthreads[i].join(); 264 } 265 } 266 267 /** 268 * Test that threads blocked waiting for klass to be initialized 269 * on getstatic bytecode release the carrier. 270 */ 271 @Test 272 void testReleaseAtKlassInitGetStatic() throws Exception { 273 class TestClass { 274 static { 275 try { 276 finishGetStatic.await(); 277 } catch(InterruptedException e) {} 278 } 279 public static int NUMBER = 150; 280 } 281 282 Thread[] vthreads = new Thread[MAX_VTHREAD_COUNT]; 283 CountDownLatch[] started = new CountDownLatch[MAX_VTHREAD_COUNT]; 284 AtomicInteger[] result = new AtomicInteger[MAX_VTHREAD_COUNT]; 285 for (int i = 0; i < MAX_VTHREAD_COUNT; i++) { 286 final int id = i; 287 started[i] = new CountDownLatch(1); 288 result[i] = new AtomicInteger(); 289 vthreads[i] = Thread.ofVirtual().start(() -> { 290 started[id].countDown(); 291 result[id].set(TestClass.NUMBER); 292 }); 293 } 294 for (int i = 0; i < MAX_VTHREAD_COUNT; i++) { 295 started[i].await(); 296 await(vthreads[i], Thread.State.WAITING); 297 } 298 299 finishGetStatic.countDown(); 300 for (int i = 0; i < MAX_VTHREAD_COUNT; i++) { 301 vthreads[i].join(); 302 assertEquals(result[i].get(), TestClass.NUMBER); 303 } 304 } 305 306 /** 307 * Test that threads blocked waiting for klass to be initialized 308 * on putstatic release the carrier. 309 */ 310 @Test 311 void testReleaseAtKlassInitPutStatic() throws Exception { 312 class TestClass { 313 static { 314 try { 315 finishPutStatic.await(); 316 } catch(InterruptedException e) {} 317 } 318 public static int NUMBER; 319 } 320 321 Thread[] vthreads = new Thread[MAX_VTHREAD_COUNT]; 322 CountDownLatch[] started = new CountDownLatch[MAX_VTHREAD_COUNT]; 323 for (int i = 0; i < MAX_VTHREAD_COUNT; i++) { 324 final int id = i; 325 started[i] = new CountDownLatch(1); 326 vthreads[i] = Thread.ofVirtual().start(() -> { 327 started[id].countDown(); 328 TestClass.NUMBER = id; 329 }); 330 } 331 for (int i = 0; i < MAX_VTHREAD_COUNT; i++) { 332 started[i].await(); 333 await(vthreads[i], Thread.State.WAITING); 334 } 335 336 finishPutStatic.countDown(); 337 for (int i = 0; i < MAX_VTHREAD_COUNT; i++) { 338 vthreads[i].join(); 339 } 340 } 341 342 /** 343 * Test that interruptions during preemption on klass init 344 * are preserved. 345 */ 346 @ParameterizedTest 347 @MethodSource("interruptTestCases") 348 void testReleaseAtKlassInitPreserverInterrupt(int timeout, Runnable m, CountDownLatch finish) throws Exception { 349 // Start vthread1 and wait until it blocks in TestClassX initializer 350 var vthread1_started = new CountDownLatch(1); 351 var vthread1 = Thread.ofVirtual().start(() -> { 352 vthread1_started.countDown(); 353 m.run(); 354 }); 355 vthread1_started.await(); 356 await(vthread1, Thread.State.WAITING); 357 358 // Start vthread2 and wait until it gets preempted on TestClassX initialization 359 var lock = new Object(); 360 var interruptedException = new AtomicBoolean(); 361 var vthread2_started = new CountDownLatch(1); 362 var vthread2 = Thread.ofVirtual().start(() -> { 363 vthread2_started.countDown(); 364 m.run(); 365 synchronized (lock) { 366 try { 367 if (timeout > 0) { 368 lock.wait(timeout); 369 } else { 370 lock.wait(); 371 } 372 } catch (InterruptedException e) { 373 // check stack trace has the expected frames 374 Set<String> expected = Set.of("wait0", "wait", "run"); 375 Set<String> methods = Stream.of(e.getStackTrace()) 376 .map(StackTraceElement::getMethodName) 377 .collect(Collectors.toSet()); 378 assertTrue(methods.containsAll(expected)); 379 interruptedException.set(true); 380 } 381 } 382 }); 383 vthread2_started.await(); 384 await(vthread2, Thread.State.WAITING); 385 386 // Interrupt vthread2 and let initialization of TestClassX finish 387 vthread2.interrupt(); 388 finish.countDown(); 389 vthread1.join(); 390 vthread2.join(); 391 assertTrue(interruptedException.get()); 392 } 393 394 static CountDownLatch finishInterrupt0 = new CountDownLatch(1); 395 class TestClass0 { 396 static { 397 try { 398 finishInterrupt0.await(); 399 } catch(InterruptedException e) {} 400 } 401 static void m() {} 402 } 403 404 static CountDownLatch finishInterrupt30000 = new CountDownLatch(1); 405 class TestClass30000 { 406 static { 407 try { 408 finishInterrupt30000.await(); 409 } catch(InterruptedException e) {} 410 } 411 static void m() {} 412 } 413 414 static CountDownLatch finishInterruptMax = new CountDownLatch(1); 415 class TestClassMax { 416 static { 417 try { 418 finishInterruptMax.await(); 419 } catch(InterruptedException e) {} 420 } 421 static void m() {} 422 } 423 424 static Stream<Arguments> interruptTestCases() { 425 return Stream.of( 426 Arguments.of(0, (Runnable) TestClass0::m, finishInterrupt0), 427 Arguments.of(30000, (Runnable) TestClass30000::m, finishInterrupt30000), 428 Arguments.of(Integer.MAX_VALUE, (Runnable) TestClassMax::m, finishInterruptMax) 429 ); 430 } 431 432 /** 433 * Test case of threads blocked waiting for klass to be initialized 434 * when the klass initialization fails. 435 */ 436 @Test 437 void testReleaseAtKlassInitFailedInit() throws Exception { 438 class TestClass { 439 static int[] a = {1, 2, 3}; 440 static { 441 try { 442 finishFailedInit.await(); 443 a[3] = 4; 444 } catch(InterruptedException e) {} 445 } 446 static void m() { 447 } 448 } 449 450 Thread[] vthreads = new Thread[MAX_VTHREAD_COUNT]; 451 CountDownLatch[] started = new CountDownLatch[MAX_VTHREAD_COUNT]; 452 for (int i = 0; i < MAX_VTHREAD_COUNT; i++) { 453 final int id = i; 454 started[i] = new CountDownLatch(1); 455 vthreads[i] = Thread.ofVirtual().start(() -> { 456 started[id].countDown(); 457 TestClass.m(); 458 }); 459 } 460 for (int i = 0; i < MAX_VTHREAD_COUNT; i++) { 461 started[i].await(); 462 await(vthreads[i], Thread.State.WAITING); 463 } 464 465 finishFailedInit.countDown(); 466 for (int i = 0; i < MAX_VTHREAD_COUNT; i++) { 467 vthreads[i].join(); 468 } 469 } 470 471 /** 472 * Waits for the given thread to reach a given state. 473 */ 474 private void await(Thread thread, Thread.State expectedState) throws InterruptedException { 475 Thread.State state = thread.getState(); 476 while (state != expectedState) { 477 assertTrue(state != Thread.State.TERMINATED, "Thread has terminated"); 478 Thread.sleep(10); 479 state = thread.getState(); 480 } 481 } 482 }