1 /*
2  * Copyright (c) 2021, 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 package test.java.lang.constant;
24 
25 import java.lang.constant.ClassDesc;
26 import java.lang.constant.ConstantDesc;
27 import java.lang.constant.DirectMethodHandleDesc;
28 import java.lang.constant.DynamicConstantDesc;
29 import java.lang.constant.MethodHandleDesc;
30 import java.lang.constant.MethodTypeDesc;
31 import java.lang.invoke.MethodHandles;
32 import java.util.ArrayList;
33 import java.util.List;
34 import java.util.concurrent.Callable;
35 import java.util.concurrent.CountDownLatch;
36 import java.util.concurrent.ExecutorService;
37 import java.util.concurrent.Executors;
38 import java.util.concurrent.Future;
39 
40 /**
41  * @test
42  * @bug 8263108
43  * @summary Verify that concurrent classloading of java.lang.constant.DynamicConstantDesc and
44  * java.lang.constant.ConstantDescs doesn't lead to a deadlock
45  * @run main/othervm DynamicConstantDescTest
46  * @run main/othervm DynamicConstantDescTest
47  * @run main/othervm DynamicConstantDescTest
48  * @run main/othervm DynamicConstantDescTest
49  * @run main/othervm DynamicConstantDescTest
50  */
51 // Implementation note: This test cannot use testng, since by the time this test gets a chance
52 // to trigger a concurrent classloading of the classes it's interested in, the testng infrastructure
53 // would already have loaded those classes in a single main thread.
54 public class DynamicConstantDescTest {
55 
56     /**
57      * Loads {@code java.lang.constant.DynamicConstantDesc} and {@code java.lang.constant.ConstantDescs}
58      * and invokes {@code java.lang.constant.DynamicConstantDesc#ofCanonical()} concurrently in a thread
59      * of their own and expects the classloading of both those classes
60      * to succeed.
61      */
main(final String[] args)62     public static void main(final String[] args) throws Exception {
63         final CountDownLatch taskTriggerLatch = new CountDownLatch(4);
64         final List<Callable<?>> tasks = new ArrayList<>();
65         // a bunch of tasks - some doing just Class.forName and some
66         // invoking DynamicConstantDesc.ofCanonical
67         tasks.add(new Task("java.lang.constant.DynamicConstantDesc", taskTriggerLatch));
68         tasks.add(new InvokeOfCanonical(taskTriggerLatch));
69         tasks.add(new Task("java.lang.constant.ConstantDescs", taskTriggerLatch));
70         tasks.add(new InvokeOfCanonical(taskTriggerLatch));
71         final ExecutorService executor = Executors.newFixedThreadPool(tasks.size());
72         try {
73             final Future<?>[] results = new Future[tasks.size()];
74             // submit
75             int i = 0;
76             for (final Callable<?> task : tasks) {
77                 results[i++] = executor.submit(task);
78             }
79             // wait for completion
80             for (i = 0; i < tasks.size(); i++) {
81                 results[i].get();
82             }
83         } finally {
84             executor.shutdownNow();
85         }
86     }
87 
88     private static class Task implements Callable<Class<?>> {
89         private final String className;
90         private final CountDownLatch latch;
91 
Task(final String className, final CountDownLatch latch)92         private Task(final String className, final CountDownLatch latch) {
93             this.className = className;
94             this.latch = latch;
95         }
96 
97         @Override
call()98         public Class<?> call() {
99             System.out.println(Thread.currentThread().getName() + " loading " + this.className);
100             try {
101                 // let the other tasks know we are ready to trigger our work
102                 latch.countDown();
103                 // wait for the other task to let us know they are ready to trigger their work too
104                 latch.await();
105                 return Class.forName(this.className);
106             } catch (Exception e) {
107                 throw new RuntimeException(e);
108             }
109         }
110     }
111 
112     enum MyEnum {A, B}
113 
114     private static class InvokeOfCanonical implements Callable<Object> {
115         private final CountDownLatch latch;
116 
InvokeOfCanonical(final CountDownLatch latch)117         private InvokeOfCanonical(final CountDownLatch latch) {
118             this.latch = latch;
119         }
120 
121         @Override
call()122         public Object call() {
123             System.out.println(Thread.currentThread().getName()
124                     + " calling  DynamicConstantDesc.ofCanonical()");
125             try {
126                 // let the other tasks know we are ready to trigger our work
127                 latch.countDown();
128                 // wait for the other task to let us know they are ready to trigger their work too
129                 latch.await();
130                 ConstantDesc desc = DynamicConstantDesc.ofCanonical(boostrapMethodForEnumConstant(),
131                         "A", ClassDesc.of("test.java.lang.constant.DynamicConstantDescTest").nested("MyEnum"),
132                         new ConstantDesc[0]);
133                 if (desc == null) {
134                     throw new Exception("DynamicConstantDesc.ofCanonical unexpectedly returned null");
135                 }
136                 if (!MyEnum.A.equals(desc.resolveConstantDesc(MethodHandles.lookup()))) {
137                     throw new Exception("DynamicConstantDesc.ofCanonical returned unexpected result " + desc);
138                 }
139                 return desc;
140             } catch (Exception e) {
141                 throw new RuntimeException(e);
142             }
143         }
144 
boostrapMethodForEnumConstant()145         private static DirectMethodHandleDesc boostrapMethodForEnumConstant() {
146             ClassDesc[] args = {ClassDesc.of("java.lang.invoke.MethodHandles").nested("Lookup"),
147                     ClassDesc.of("java.lang.String"),
148                     ClassDesc.of("java.lang.Class")};
149             return MethodHandleDesc.ofMethod(java.lang.constant.DirectMethodHandleDesc.Kind.STATIC,
150                     ClassDesc.of("java.lang.invoke.ConstantBootstraps"),
151                     "enumConstant", MethodTypeDesc.of(ClassDesc.of("java.lang.Enum"), new ClassDesc[0])
152                             .insertParameterTypes(0, args));
153         }
154 
155     }
156 
157 }