1/*
2 * Copyright (c) 2013, 2016, 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 * @bug 8022595
27 * @summary JSR292: deadlock during class loading of MethodHandles, MethodHandleImpl & MethodHandleNatives
28 * @library /test/lib /
29 * @modules java.base/jdk.internal.misc
30 *          java.management
31 *
32 * @run main/othervm compiler.jsr292.ConcurrentClassLoadingTest
33 */
34
35package compiler.jsr292;
36
37import jdk.test.lib.Utils;
38
39import java.util.ArrayList;
40import java.util.Arrays;
41import java.util.List;
42import java.util.Random;
43import java.util.concurrent.BrokenBarrierException;
44import java.util.concurrent.CyclicBarrier;
45
46public class ConcurrentClassLoadingTest {
47    int numThreads = 0;
48    CyclicBarrier l;
49    private static final Random rand = Utils.getRandomInstance();
50
51    public static void main(String[] args) throws Throwable {
52        ConcurrentClassLoadingTest test = new ConcurrentClassLoadingTest();
53        test.parseArgs(args);
54        test.run();
55    }
56
57    void parseArgs(String[] args) {
58        int i = 0;
59        while (i < args.length) {
60            String flag = args[i];
61            switch(flag) {
62                case "-numThreads":
63                    numThreads = Integer.parseInt(args[++i]);
64                    break;
65                default:
66                    throw new Error("Unknown flag: " + flag);
67            }
68            ++i;
69        }
70    }
71
72    void init() {
73        if (numThreads == 0) {
74            numThreads = Runtime.getRuntime().availableProcessors();
75        }
76
77        l = new CyclicBarrier(numThreads + 1);
78
79        System.out.printf("Threads: %d\n", numThreads);
80    }
81
82    final List<Loader> loaders = new ArrayList<>();
83
84    void prepare() {
85        List<String> c = new ArrayList<>(Arrays.asList(classNames));
86
87        // Split classes between loading threads
88        int count = (classNames.length / numThreads) + 1;
89        for (int t = 0; t < numThreads; t++) {
90            List<String> sel = new ArrayList<>();
91
92            System.out.printf("Thread #%d:\n", t);
93            for (int i = 0; i < count; i++) {
94                if (c.isEmpty()) {
95                    break;
96                }
97
98                int k = rand.nextInt(c.size());
99                String elem = c.remove(k);
100                sel.add(elem);
101                System.out.printf("\t%s\n", elem);
102            }
103            loaders.add(new Loader(sel));
104        }
105
106        // Print diagnostic info when the test hangs
107        Runtime.getRuntime().addShutdownHook(new Thread() {
108            public void run() {
109                boolean alive = false;
110                for (Loader l : loaders) {
111                    if (!l.isAlive())  continue;
112
113                    if (!alive) {
114                        System.out.println("Some threads are still alive:");
115                        alive = true;
116                    }
117
118                    System.out.println(l.getName());
119                    for (StackTraceElement elem : l.getStackTrace()) {
120                        System.out.println("\t"+elem.toString());
121                    }
122                }
123            }
124        });
125    }
126
127    public void run() throws Throwable {
128        init();
129        prepare();
130
131        for (Loader loader : loaders) {
132            loader.start();
133        }
134
135        l.await();
136
137        for (Loader loader : loaders) {
138            loader.join();
139        }
140    }
141
142    class Loader extends Thread {
143        List<String> classes;
144
145        public Loader(List<String> classes) {
146            this.classes = classes;
147            setDaemon(true);
148        }
149
150        @Override
151        public void run() {
152            try {
153                l.await();
154
155                for (String name : classes) {
156                    Class.forName(name).getName();
157                }
158            } catch (ClassNotFoundException | BrokenBarrierException | InterruptedException e) {
159                throw new Error(e);
160            }
161        }
162    }
163
164    final static String[] classNames = {
165            "java.lang.invoke.CallSite",
166            "java.lang.invoke.ConstantCallSite",
167            "java.lang.invoke.LambdaConversionException",
168            "java.lang.invoke.LambdaMetafactory",
169            "java.lang.invoke.MethodHandle",
170            "java.lang.invoke.MethodHandleInfo",
171            "java.lang.invoke.MethodHandleProxies",
172            "java.lang.invoke.MethodHandles",
173            "java.lang.invoke.MethodType",
174            "java.lang.invoke.MutableCallSite",
175            "java.lang.invoke.SerializedLambda",
176            "java.lang.invoke.SwitchPoint",
177            "java.lang.invoke.VolatileCallSite",
178            "java.lang.invoke.WrongMethodTypeException"
179    };
180}
181