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