1 /* 2 * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* 25 * @test 26 * @library /test/lib 27 * 28 * @enablePreview 29 * 30 * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:-TieredCompilation -DTHROW=false -Xcheck:jni ClassInitBarrier 31 * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:-TieredCompilation -DTHROW=true -Xcheck:jni ClassInitBarrier 32 * 33 * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:TieredStopAtLevel=1 -DTHROW=false -XX:CompileCommand=dontinline,*::static* -Xcheck:jni ClassInitBarrier 34 * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:TieredStopAtLevel=1 -DTHROW=true -XX:CompileCommand=dontinline,*::static* -Xcheck:jni ClassInitBarrier 35 * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:-TieredCompilation -DTHROW=false -XX:CompileCommand=dontinline,*::static* -Xcheck:jni ClassInitBarrier 36 * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:-TieredCompilation -DTHROW=true -XX:CompileCommand=dontinline,*::static* -Xcheck:jni ClassInitBarrier 37 * 38 * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:TieredStopAtLevel=1 -DTHROW=false -XX:CompileCommand=exclude,*::static* -Xcheck:jni ClassInitBarrier 39 * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:TieredStopAtLevel=1 -DTHROW=true -XX:CompileCommand=exclude,*::static* -Xcheck:jni ClassInitBarrier 40 * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:-TieredCompilation -DTHROW=false -XX:CompileCommand=exclude,*::static* -Xcheck:jni ClassInitBarrier 41 * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:-TieredCompilation -DTHROW=true -XX:CompileCommand=exclude,*::static* -Xcheck:jni ClassInitBarrier 42 */ 43 44 import jdk.test.lib.Asserts; 45 46 import java.util.*; 47 import java.util.concurrent.atomic.AtomicBoolean; 48 import java.util.concurrent.atomic.AtomicInteger; 49 import java.util.function.Consumer; 50 51 public class ClassInitBarrier { 52 static { 53 System.loadLibrary("ClassInitBarrier"); 54 55 if (!init()) { 56 throw new Error("init failed"); 57 } 58 } 59 60 static native boolean init(); 61 62 static final boolean THROW = Boolean.getBoolean("THROW"); 63 64 static value class MyValue { 65 int x = 42; 66 67 void verify() { 68 Asserts.assertEquals(x, 42); 69 } 70 } 71 72 static class Test { 73 74 static class A { 75 static { 76 if (!init(B.class)) { 77 throw new Error("init failed"); 78 } 79 80 changePhase(Phase.IN_PROGRESS); 81 runTests(); // interpreted mode 82 warmup(); // trigger compilation 83 runTests(); // compiled mode 84 85 ensureBlocked(); // ensure still blocked 86 maybeThrow(); // fail initialization if needed 87 88 changePhase(Phase.FINISHED); 89 } 90 91 static void staticM(Runnable action, MyValue val) { action.run(); val.verify(); } 92 static synchronized void staticS(Runnable action, MyValue val) { action.run(); val.verify(); } 93 static native void staticN(Runnable action, MyValue val); 94 95 static int staticF; 96 97 int f; 98 void m() {} 99 100 static native boolean init(Class<B> cls); 101 } 102 103 static class B extends A {} 104 105 static void testInvokeStatic(Runnable action, MyValue val) { A.staticM(action, val); } 106 static void testInvokeStaticSync(Runnable action, MyValue val) { A.staticS(action, val); } 107 static void testInvokeStaticNative(Runnable action, MyValue val) { A.staticN(action, val); } 108 109 static int testGetStatic(Runnable action, MyValue val) { int v = A.staticF; action.run(); val.verify(); return v; } 110 static void testPutStatic(Runnable action, MyValue val) { A.staticF = 1; action.run(); val.verify(); } 111 static A testNewInstanceA(Runnable action, MyValue val) { A obj = new A(); action.run(); val.verify(); return obj; } 112 static B testNewInstanceB(Runnable action, MyValue val) { B obj = new B(); action.run(); val.verify(); return obj; } 113 114 static int testGetField(A recv, Runnable action, MyValue val) { int v = recv.f; action.run(); val.verify(); return v; } 115 static void testPutField(A recv, Runnable action, MyValue val) { recv.f = 1; action.run(); val.verify(); } 116 static void testInvokeVirtual(A recv, Runnable action, MyValue val) { recv.m(); action.run(); val.verify(); } 117 118 static native void testInvokeStaticJNI(Runnable action, MyValue val); 119 static native void testInvokeStaticSyncJNI(Runnable action, MyValue val); 120 static native void testInvokeStaticNativeJNI(Runnable action, MyValue val); 121 122 static native int testGetStaticJNI(Runnable action, MyValue val); 123 static native void testPutStaticJNI(Runnable action, MyValue val); 124 static native A testNewInstanceAJNI(Runnable action, MyValue val); 125 static native B testNewInstanceBJNI(Runnable action, MyValue val); 126 127 static native int testGetFieldJNI(A recv, Runnable action, MyValue val); 128 static native void testPutFieldJNI(A recv, Runnable action, MyValue val); 129 static native void testInvokeVirtualJNI(A recv, Runnable action, MyValue val); 130 131 static void runTests() { 132 checkBlockingAction(Test::testInvokeStatic); // invokestatic 133 checkBlockingAction(Test::testInvokeStaticSync); // invokestatic 134 checkBlockingAction(Test::testInvokeStaticNative); // invokestatic 135 checkBlockingAction(Test::testGetStatic); // getstatic 136 checkBlockingAction(Test::testPutStatic); // putstatic 137 checkBlockingAction(Test::testNewInstanceA); // new 138 139 checkNonBlockingAction(Test::testInvokeStaticJNI); // invokestatic 140 checkNonBlockingAction(Test::testInvokeStaticSyncJNI); // invokestatic 141 checkNonBlockingAction(Test::testInvokeStaticNativeJNI); // invokestatic 142 checkNonBlockingAction(Test::testGetStaticJNI); // getstatic 143 checkNonBlockingAction(Test::testPutStaticJNI); // putstatic 144 checkBlockingAction(Test::testNewInstanceAJNI); // new 145 146 A recv = testNewInstanceB(NON_BLOCKING.get(), new MyValue()); // trigger B initialization 147 checkNonBlockingAction(Test::testNewInstanceB); // new: NO BLOCKING: same thread: A being initialized, B fully initialized 148 149 checkNonBlockingAction(recv, Test::testGetField); // getfield 150 checkNonBlockingAction(recv, Test::testPutField); // putfield 151 checkNonBlockingAction(recv, Test::testInvokeVirtual); // invokevirtual 152 153 checkNonBlockingAction(Test::testNewInstanceBJNI); // new: NO BLOCKING: same thread: A being initialized, B fully initialized 154 checkNonBlockingAction(recv, Test::testGetFieldJNI); // getfield 155 checkNonBlockingAction(recv, Test::testPutFieldJNI); // putfield 156 checkNonBlockingAction(recv, Test::testInvokeVirtualJNI); // invokevirtual 157 } 158 159 static void warmup() { 160 MyValue val = new MyValue(); 161 for (int i = 0; i < 20_000; i++) { 162 testInvokeStatic( NON_BLOCKING_WARMUP, val); 163 testInvokeStaticNative(NON_BLOCKING_WARMUP, val); 164 testInvokeStaticSync( NON_BLOCKING_WARMUP, val); 165 testGetStatic( NON_BLOCKING_WARMUP, val); 166 testPutStatic( NON_BLOCKING_WARMUP, val); 167 testNewInstanceA( NON_BLOCKING_WARMUP, val); 168 testNewInstanceB( NON_BLOCKING_WARMUP, val); 169 170 testGetField(new B(), NON_BLOCKING_WARMUP, val); 171 testPutField(new B(), NON_BLOCKING_WARMUP, val); 172 testInvokeVirtual(new B(), NON_BLOCKING_WARMUP, val); 173 } 174 } 175 176 static void run() { 177 execute(ExceptionInInitializerError.class, () -> triggerInitialization(A.class)); 178 ensureFinished(); 179 runTests(); // after initialization is over 180 } 181 } 182 183 // ============================================================================================================== // 184 185 static void execute(Class<? extends Throwable> expectedExceptionClass, Runnable action) { 186 try { 187 action.run(); 188 if (THROW) throw failure("no exception thrown"); 189 } catch (Throwable e) { 190 if (THROW) { 191 if (e.getClass() == expectedExceptionClass) { 192 // expected 193 } else { 194 String msg = String.format("unexpected exception thrown: expected %s, caught %s", 195 expectedExceptionClass.getName(), e); 196 throw failure(msg, e); 197 } 198 } else { 199 throw failure("no exception expected", e); 200 } 201 } 202 } 203 204 private static AssertionError failure(String msg) { 205 return new AssertionError(phase + ": " + msg); 206 } 207 208 private static AssertionError failure(String msg, Throwable e) { 209 return new AssertionError(phase + ": " + msg, e); 210 } 211 212 static final List<Thread> BLOCKED_THREADS = Collections.synchronizedList(new ArrayList<>()); 213 static final Consumer<Thread> ON_BLOCK = BLOCKED_THREADS::add; 214 215 static final Map<Thread,Throwable> FAILED_THREADS = Collections.synchronizedMap(new HashMap<>()); 216 static final Thread.UncaughtExceptionHandler ON_FAILURE = FAILED_THREADS::put; 217 218 private static void ensureBlocked() { 219 for (Thread thr : BLOCKED_THREADS) { 220 try { 221 thr.join(100); 222 if (!thr.isAlive()) { 223 dump(thr); 224 throw new AssertionError("not blocked"); 225 } 226 } catch (InterruptedException e) { 227 throw new Error(e); 228 } 229 } 230 } 231 232 233 private static void ensureFinished() { 234 for (Thread thr : BLOCKED_THREADS) { 235 try { 236 thr.join(15_000); 237 } catch (InterruptedException e) { 238 throw new Error(e); 239 } 240 if (thr.isAlive()) { 241 dump(thr); 242 throw new AssertionError(thr + ": still blocked"); 243 } 244 } 245 for (Thread thr : BLOCKED_THREADS) { 246 if (THROW) { 247 if (!FAILED_THREADS.containsKey(thr)) { 248 throw new AssertionError(thr + ": exception not thrown"); 249 } 250 251 Throwable ex = FAILED_THREADS.get(thr); 252 if (ex.getClass() != NoClassDefFoundError.class) { 253 throw new AssertionError(thr + ": wrong exception thrown", ex); 254 } 255 } else { 256 if (FAILED_THREADS.containsKey(thr)) { 257 Throwable ex = FAILED_THREADS.get(thr); 258 throw new AssertionError(thr + ": exception thrown", ex); 259 } 260 } 261 } 262 if (THROW) { 263 Asserts.assertEquals(BLOCKING_COUNTER.get(), 0); 264 } else { 265 Asserts.assertEquals(BLOCKING_COUNTER.get(), BLOCKING_ACTIONS.get()); 266 } 267 268 dumpInfo(); 269 } 270 271 interface TestCase0 { 272 void run(Runnable runnable, MyValue val); 273 } 274 275 interface TestCase1<T> { 276 void run(T arg, Runnable runnable, MyValue val); 277 } 278 279 enum Phase { BEFORE_INIT, IN_PROGRESS, FINISHED, INIT_FAILURE } 280 281 static volatile Phase phase = Phase.BEFORE_INIT; 282 283 static void changePhase(Phase newPhase) { 284 dumpInfo(); 285 286 Phase oldPhase = phase; 287 switch (oldPhase) { 288 case BEFORE_INIT: 289 Asserts.assertEquals(NON_BLOCKING_ACTIONS.get(), 0); 290 Asserts.assertEquals(NON_BLOCKING_COUNTER.get(), 0); 291 292 Asserts.assertEquals(BLOCKING_ACTIONS.get(), 0); 293 Asserts.assertEquals(BLOCKING_COUNTER.get(), 0); 294 break; 295 case IN_PROGRESS: 296 Asserts.assertEquals(NON_BLOCKING_COUNTER.get(), NON_BLOCKING_ACTIONS.get()); 297 298 Asserts.assertEquals(BLOCKING_COUNTER.get(), 0); 299 break; 300 default: throw new Error("wrong phase transition " + oldPhase); 301 } 302 phase = newPhase; 303 } 304 305 static void dumpInfo() { 306 System.out.println("Phase: " + phase); 307 System.out.println("Non-blocking actions: " + NON_BLOCKING_COUNTER.get() + " / " + NON_BLOCKING_ACTIONS.get()); 308 System.out.println("Blocking actions: " + BLOCKING_COUNTER.get() + " / " + BLOCKING_ACTIONS.get()); 309 } 310 311 static final Runnable NON_BLOCKING_WARMUP = () -> { 312 if (phase != Phase.IN_PROGRESS) { 313 throw new AssertionError("NON_BLOCKING: wrong phase: " + phase); 314 } 315 }; 316 317 static Runnable disposableAction(final Phase validPhase, final AtomicInteger invocationCounter, final AtomicInteger actionCounter) { 318 actionCounter.incrementAndGet(); 319 320 final AtomicBoolean cnt = new AtomicBoolean(false); 321 return () -> { 322 if (cnt.getAndSet(true)) { 323 throw new Error("repeated invocation"); 324 } 325 invocationCounter.incrementAndGet(); 326 if (phase != validPhase) { 327 throw new AssertionError("NON_BLOCKING: wrong phase: " + phase); 328 } 329 }; 330 } 331 332 @FunctionalInterface 333 interface Factory<V> { 334 V get(); 335 } 336 337 static final AtomicInteger NON_BLOCKING_COUNTER = new AtomicInteger(0); 338 static final AtomicInteger NON_BLOCKING_ACTIONS = new AtomicInteger(0); 339 static final Factory<Runnable> NON_BLOCKING = () -> disposableAction(phase, NON_BLOCKING_COUNTER, NON_BLOCKING_ACTIONS); 340 341 static final AtomicInteger BLOCKING_COUNTER = new AtomicInteger(0); 342 static final AtomicInteger BLOCKING_ACTIONS = new AtomicInteger(0); 343 static final Factory<Runnable> BLOCKING = () -> disposableAction(Phase.FINISHED, BLOCKING_COUNTER, BLOCKING_ACTIONS); 344 345 static void checkBlockingAction(TestCase0 r) { 346 MyValue val = new MyValue(); 347 switch (phase) { 348 case IN_PROGRESS: { 349 // Barrier during class initalization. 350 r.run(NON_BLOCKING.get(), val); // initializing thread 351 checkBlocked(ON_BLOCK, ON_FAILURE, r); // different thread 352 break; 353 } 354 case FINISHED: { 355 // No barrier after class initalization is over. 356 r.run(NON_BLOCKING.get(), val); // initializing thread 357 checkNotBlocked(r); // different thread 358 break; 359 } 360 case INIT_FAILURE: { 361 // Exception is thrown after class initialization failed. 362 TestCase0 test = (action, valarg) -> execute(NoClassDefFoundError.class, () -> r.run(action, valarg)); 363 364 test.run(NON_BLOCKING.get(), val); // initializing thread 365 checkNotBlocked(test); // different thread 366 break; 367 } 368 default: throw new Error("wrong phase: " + phase); 369 } 370 } 371 372 static void checkNonBlockingAction(TestCase0 r) { 373 r.run(NON_BLOCKING.get(), new MyValue()); // initializing thread 374 checkNotBlocked(r); // different thread 375 } 376 377 static <T> void checkNonBlockingAction(T recv, TestCase1<T> r) { 378 r.run(recv, NON_BLOCKING.get(), new MyValue()); // initializing thread 379 checkNotBlocked((action, val) -> r.run(recv, action, val)); // different thread 380 } 381 382 static void checkFailingAction(TestCase0 r) { 383 r.run(NON_BLOCKING.get(), new MyValue()); // initializing thread 384 checkNotBlocked(r); // different thread 385 } 386 387 static void triggerInitialization(Class<?> cls) { 388 try { 389 Class<?> loadedClass = Class.forName(cls.getName(), true, cls.getClassLoader()); 390 if (loadedClass != cls) { 391 throw new Error("wrong class"); 392 } 393 } catch (ClassNotFoundException e) { 394 throw new Error(e); 395 } 396 } 397 398 static void checkBlocked(Consumer<Thread> onBlockHandler, Thread.UncaughtExceptionHandler onException, TestCase0 r) { 399 Thread thr = new Thread(() -> { 400 try { 401 r.run(BLOCKING.get(), new MyValue()); 402 System.out.println("Thread " + Thread.currentThread() + ": Finished successfully"); 403 } catch(Throwable e) { 404 System.out.println("Thread " + Thread.currentThread() + ": Exception thrown: " + e); 405 if (!THROW) { 406 e.printStackTrace(); 407 } 408 throw e; 409 } 410 } ); 411 thr.setUncaughtExceptionHandler(onException); 412 413 thr.start(); 414 try { 415 thr.join(100); 416 417 dump(thr); 418 if (thr.isAlive()) { 419 onBlockHandler.accept(thr); // blocked 420 } else { 421 throw new AssertionError("not blocked"); 422 } 423 } catch (InterruptedException e) { 424 throw new Error(e); 425 } 426 } 427 428 static void checkNotBlocked(TestCase0 r) { 429 final Thread thr = new Thread(() -> r.run(NON_BLOCKING.get(), new MyValue())); 430 final Throwable[] ex = new Throwable[1]; 431 thr.setUncaughtExceptionHandler((t, e) -> { 432 if (thr != t) { 433 ex[0] = new Error("wrong thread: " + thr + " vs " + t); 434 } else { 435 ex[0] = e; 436 } 437 }); 438 439 thr.start(); 440 try { 441 thr.join(15_000); 442 if (thr.isAlive()) { 443 dump(thr); 444 throw new AssertionError("blocked"); 445 } 446 } catch (InterruptedException e) { 447 throw new Error(e); 448 } 449 450 if (ex[0] != null) { 451 throw new AssertionError("no exception expected", ex[0]); 452 } 453 } 454 455 static void maybeThrow() { 456 if (THROW) { 457 changePhase(Phase.INIT_FAILURE); 458 throw new RuntimeException("failed class initialization"); 459 } 460 } 461 462 private static void dump(Thread thr) { 463 System.out.println("Thread: " + thr); 464 System.out.println("Thread state: " + thr.getState()); 465 if (thr.isAlive()) { 466 for (StackTraceElement frame : thr.getStackTrace()) { 467 System.out.println(frame); 468 } 469 } else { 470 if (FAILED_THREADS.containsKey(thr)) { 471 System.out.println("Failed with an exception: "); 472 FAILED_THREADS.get(thr).toString(); 473 } else { 474 System.out.println("Finished successfully"); 475 } 476 } 477 } 478 479 public static void main(String[] args) throws Exception { 480 Test.run(); 481 System.out.println("TEST PASSED"); 482 } 483 } --- EOF ---