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