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