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