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 144 ensureFinished(); 145 } 146 } 147 148 // ============================================================================================================== // 149 150 static void execute(Class<? extends Throwable> expectedExceptionClass, Runnable action) { 151 try { 152 action.run(); 153 if (THROW) throw new AssertionError("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 new AssertionError(msg, e); 162 } 163 } else { 164 throw new AssertionError("no exception expected", e); 165 } 166 } 167 } 168 169 static final List<Thread> BLOCKED_THREADS = Collections.synchronizedList(new ArrayList<>()); 170 static final Consumer<Thread> ON_BLOCK = BLOCKED_THREADS::add; 171 172 static final Map<Thread,Throwable> FAILED_THREADS = Collections.synchronizedMap(new HashMap<>()); 173 static final Thread.UncaughtExceptionHandler ON_FAILURE = FAILED_THREADS::put; 174 175 private static void ensureBlocked() { 176 for (Thread thr : BLOCKED_THREADS) { 177 try { 178 thr.join(100); 179 if (!thr.isAlive()) { 180 dump(thr); 181 throw new AssertionError("not blocked"); 182 } 183 } catch (InterruptedException e) { 184 throw new Error(e); 185 } 186 } 187 } 188 189 190 private static void ensureFinished() { 191 for (Thread thr : BLOCKED_THREADS) { 192 try { 193 thr.join(15_000); 194 } catch (InterruptedException e) { 195 throw new Error(e); 196 } 197 if (thr.isAlive()) { 198 dump(thr); 199 throw new AssertionError(thr + ": still blocked"); 200 } 201 } 202 for (Thread thr : BLOCKED_THREADS) { 203 if (THROW) { 204 if (!FAILED_THREADS.containsKey(thr)) { 205 throw new AssertionError(thr + ": exception not thrown"); 206 } 207 208 Throwable ex = FAILED_THREADS.get(thr); 209 if (ex.getClass() != NoClassDefFoundError.class) { 210 throw new AssertionError(thr + ": wrong exception thrown", ex); 211 } 212 } else { 213 if (FAILED_THREADS.containsKey(thr)) { 214 Throwable ex = FAILED_THREADS.get(thr); 215 throw new AssertionError(thr + ": exception thrown", ex); 216 } 217 } 218 } 219 if (THROW) { 220 Asserts.assertEquals(BLOCKING_COUNTER.get(), 0); 221 } else { 222 Asserts.assertEquals(BLOCKING_COUNTER.get(), BLOCKING_ACTIONS.get()); 223 } 224 225 dumpInfo(); 226 } 227 228 interface TestCase0 { 229 void run(Runnable runnable); 230 } 231 232 interface TestCase1<T> { 233 void run(T arg, Runnable runnable); 234 } 235 236 enum Phase { BEFORE_INIT, IN_PROGRESS, FINISHED, INIT_FAILURE } 237 238 static volatile Phase phase = Phase.BEFORE_INIT; 239 240 static void changePhase(Phase newPhase) { 241 dumpInfo(); 242 243 Phase oldPhase = phase; 244 switch (oldPhase) { 245 case BEFORE_INIT: 246 Asserts.assertEquals(NON_BLOCKING_ACTIONS.get(), 0); 247 Asserts.assertEquals(NON_BLOCKING_COUNTER.get(), 0); 248 249 Asserts.assertEquals(BLOCKING_ACTIONS.get(), 0); 250 Asserts.assertEquals(BLOCKING_COUNTER.get(), 0); 251 break; 252 case IN_PROGRESS: 253 Asserts.assertEquals(NON_BLOCKING_COUNTER.get(), NON_BLOCKING_ACTIONS.get()); 254 255 Asserts.assertEquals(BLOCKING_COUNTER.get(), 0); 256 break; 257 default: throw new Error("wrong phase transition " + oldPhase); 258 } 259 phase = newPhase; 260 } 261 262 static void dumpInfo() { 263 System.out.println("Phase: " + phase); 264 System.out.println("Non-blocking actions: " + NON_BLOCKING_COUNTER.get() + " / " + NON_BLOCKING_ACTIONS.get()); 265 System.out.println("Blocking actions: " + BLOCKING_COUNTER.get() + " / " + BLOCKING_ACTIONS.get()); 266 } 267 268 static final Runnable NON_BLOCKING_WARMUP = () -> { 269 if (phase != Phase.IN_PROGRESS) { 270 throw new AssertionError("NON_BLOCKING: wrong phase: " + phase); 271 } 272 }; 273 274 static Runnable disposableAction(final Phase validPhase, final AtomicInteger invocationCounter, final AtomicInteger actionCounter) { 275 actionCounter.incrementAndGet(); 276 277 final AtomicBoolean cnt = new AtomicBoolean(false); 278 return () -> { 279 if (cnt.getAndSet(true)) { 280 throw new Error("repeated invocation"); 281 } 282 invocationCounter.incrementAndGet(); 283 if (phase != validPhase) { 284 throw new AssertionError("NON_BLOCKING: wrong phase: " + phase); 285 } 286 }; 287 } 288 289 @FunctionalInterface 290 interface Factory<V> { 291 V get(); 292 } 293 294 static final AtomicInteger NON_BLOCKING_COUNTER = new AtomicInteger(0); 295 static final AtomicInteger NON_BLOCKING_ACTIONS = new AtomicInteger(0); 296 static final Factory<Runnable> NON_BLOCKING = () -> disposableAction(Phase.IN_PROGRESS, NON_BLOCKING_COUNTER, NON_BLOCKING_ACTIONS); 297 298 static final AtomicInteger BLOCKING_COUNTER = new AtomicInteger(0); 299 static final AtomicInteger BLOCKING_ACTIONS = new AtomicInteger(0); 300 static final Factory<Runnable> BLOCKING = () -> disposableAction(Phase.FINISHED, BLOCKING_COUNTER, BLOCKING_ACTIONS); 301 302 static void checkBlockingAction(TestCase0 r) { 303 r.run(NON_BLOCKING.get()); // same thread 304 checkBlocked(ON_BLOCK, ON_FAILURE, r); // different thread 305 } 306 307 static void checkNonBlockingAction(TestCase0 r) { 308 r.run(NON_BLOCKING.get()); 309 checkNotBlocked(r); // different thread 310 } 311 312 static <T> void checkNonBlockingAction(T recv, TestCase1<T> r) { 313 r.run(recv, NON_BLOCKING.get()); // same thread 314 checkNotBlocked((action) -> r.run(recv, action)); // different thread 315 } 316 317 static void triggerInitialization(Class<?> cls) { 318 try { 319 Class<?> loadedClass = Class.forName(cls.getName(), true, cls.getClassLoader()); 320 if (loadedClass != cls) { 321 throw new Error("wrong class"); 322 } 323 } catch (ClassNotFoundException e) { 324 throw new Error(e); 325 } 326 } 327 328 static void checkBlocked(Consumer<Thread> onBlockHandler, Thread.UncaughtExceptionHandler onException, TestCase0 r) { 329 Thread thr = new Thread(() -> { 330 try { 331 r.run(BLOCKING.get()); 332 System.out.println("Thread " + Thread.currentThread() + ": Finished successfully"); 333 } catch(Throwable e) { 334 System.out.println("Thread " + Thread.currentThread() + ": Exception thrown: " + e); 335 if (!THROW) { 336 e.printStackTrace(); 337 } 338 throw e; 339 } 340 } ); 341 thr.setUncaughtExceptionHandler(onException); 342 343 thr.start(); 344 try { 345 thr.join(100); 346 347 dump(thr); 348 if (thr.isAlive()) { 349 onBlockHandler.accept(thr); // blocked 350 } else { 351 throw new AssertionError("not blocked"); 352 } 353 } catch (InterruptedException e) { 354 throw new Error(e); 355 } 356 } 357 358 static void checkNotBlocked(TestCase0 r) { 359 Thread thr = new Thread(() -> r.run(NON_BLOCKING.get())); 360 361 thr.start(); 362 try { 363 thr.join(15_000); 364 if (thr.isAlive()) { 365 dump(thr); 366 throw new AssertionError("blocked"); 367 } 368 } catch (InterruptedException e) { 369 throw new Error(e); 370 } 371 } 372 373 static void maybeThrow() { 374 if (THROW) { 375 changePhase(Phase.INIT_FAILURE); 376 throw new RuntimeException("failed class initialization"); 377 } 378 } 379 380 private static void dump(Thread thr) { 381 System.out.println("Thread: " + thr); 382 System.out.println("Thread state: " + thr.getState()); 383 if (thr.isAlive()) { 384 for (StackTraceElement frame : thr.getStackTrace()) { 385 System.out.println(frame); 386 } 387 } else { 388 if (FAILED_THREADS.containsKey(thr)) { 389 System.out.println("Failed with an exception: "); 390 FAILED_THREADS.get(thr).toString(); 391 } else { 392 System.out.println("Finished successfully"); 393 } 394 } 395 } 396 397 public static void main(String[] args) throws Exception { 398 Test.run(); 399 System.out.println("TEST PASSED"); 400 } 401 }