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 }