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 */ 24 25 /* 26 * @test 27 * @bug 8251945 28 * @summary Ensure no race on installing class loader data 29 * 30 * @run main/othervm Test 31 */ 32 import jdk.jfr.Event; 33 import java.util.concurrent.BrokenBarrierException; 34 import java.io.InputStream; 35 import java.io.IOException; 36 import java.io.UncheckedIOException; 37 import java.io.OutputStream; 38 import java.io.ByteArrayOutputStream; 39 import java.util.concurrent.ExecutorService; 40 import java.util.concurrent.CyclicBarrier; 41 import java.util.concurrent.Executors; 42 43 public final class Test 44 { 45 private static final int ITERATIONS = Integer.getInteger("iterations", 10000); 46 private volatile ClassLoader nextLoader; 47 main(final String[] args)48 public static void main(final String[] args) { 49 new Test().crash(); 50 } 51 crash()52 public void crash() { 53 final byte[] runnableClass = loadBytecode("Test$TestRunnable"); 54 final byte[] eventClass = loadBytecode("Test$TestRunnable$RunnableEvent"); 55 final int numberOfThreads = Runtime.getRuntime().availableProcessors(); 56 if (numberOfThreads < 1) { 57 throw new IllegalStateException("requies more than one thread"); 58 } 59 final ExecutorService threadPool = Executors.newFixedThreadPool(numberOfThreads); 60 final CyclicBarrier cyclicBarrier = new CyclicBarrier(numberOfThreads, () -> this.nextLoader = new PredefinedClassLoader(runnableClass, eventClass)); 61 for (int i = 0; i < numberOfThreads; ++i) { 62 threadPool.submit(new LoadingRunnable(cyclicBarrier)); 63 } 64 threadPool.shutdown(); 65 } 66 loadTestRunnable(final ClassLoader classLoader)67 Runnable loadTestRunnable(final ClassLoader classLoader) { 68 try { 69 return (Runnable)Class.forName("Test$TestRunnable", true, classLoader).asSubclass(Runnable.class).getConstructor((Class<?>[])new Class[0]).newInstance(new Object[0]); 70 } 71 catch (ReflectiveOperationException e) { 72 throw new RuntimeException("could not load runnable", e); 73 } 74 } 75 loadBytecode(final String className)76 private static byte[] loadBytecode(final String className) { 77 final String resource = toResourceName(className); 78 final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 79 try { 80 final InputStream inputStream = Test.class.getClassLoader().getResourceAsStream(resource); 81 try { 82 inputStream.transferTo(buffer); 83 if (inputStream != null) { 84 inputStream.close(); 85 } 86 } 87 catch (Throwable t) { 88 if (inputStream != null) { 89 try { 90 inputStream.close(); 91 } 92 catch (Throwable exception) { 93 t.addSuppressed(exception); 94 } 95 } 96 throw t; 97 } 98 } 99 catch (IOException e) { 100 throw new UncheckedIOException(className, e); 101 } 102 return buffer.toByteArray(); 103 } 104 toResourceName(final String className)105 private static String toResourceName(final String className) { 106 return className.replace('.', '/') + ".class"; 107 } 108 109 final class LoadingRunnable implements Runnable 110 { 111 private final CyclicBarrier barrier; 112 LoadingRunnable(final CyclicBarrier barrier)113 LoadingRunnable(final CyclicBarrier barrier) { 114 this.barrier = barrier; 115 } 116 117 @Override run()118 public void run() { 119 int itr = 0; 120 try { 121 while (itr++ < ITERATIONS) { 122 this.barrier.await(); 123 final Runnable runnable = Test.this.loadTestRunnable(Test.this.nextLoader); 124 runnable.run(); 125 } 126 } 127 catch (InterruptedException | BrokenBarrierException ex) { 128 final Exception e = ex; 129 } 130 } 131 } 132 133 static final class PredefinedClassLoader extends ClassLoader 134 { 135 private final byte[] runnableClass; 136 private final byte[] eventClass; 137 PredefinedClassLoader(final byte[] runnableClass, final byte[] eventClass)138 PredefinedClassLoader(final byte[] runnableClass, final byte[] eventClass) { 139 super(null); 140 this.runnableClass = runnableClass; 141 this.eventClass = eventClass; 142 } 143 144 @Override loadClass(final String className, final boolean resolve)145 protected Class<?> loadClass(final String className, final boolean resolve) throws ClassNotFoundException { 146 final Class<?> loadedClass = this.findLoadedClass(className); 147 if (loadedClass != null) { 148 if (resolve) { 149 this.resolveClass(loadedClass); 150 } 151 return loadedClass; 152 } 153 if (className.equals("Test$TestRunnable")) { 154 return this.loadClassFromByteArray(className, resolve, this.runnableClass); 155 } 156 if (className.equals("Test$TestRunnable$RunnableEvent")) { 157 return this.loadClassFromByteArray(className, resolve, this.eventClass); 158 } 159 return super.loadClass(className, resolve); 160 } 161 loadClassFromByteArray(final String className, final boolean resolve, final byte[] byteCode)162 private Class<?> loadClassFromByteArray(final String className, final boolean resolve, final byte[] byteCode) throws ClassNotFoundException { 163 Class<?> clazz; 164 try { 165 synchronized (getClassLoadingLock(className)) { 166 clazz = this.defineClass(className, byteCode, 0, byteCode.length); 167 } 168 } 169 catch (LinkageError e) { 170 clazz = this.findLoadedClass(className); 171 } 172 if (resolve) { 173 this.resolveClass(clazz); 174 } 175 return clazz; 176 } 177 } 178 179 public static final class TestRunnable implements Runnable 180 { 181 @Override run()182 public void run() { 183 final RunnableEvent event = new RunnableEvent(); 184 event.setRunnableClassName("TestRunnable"); 185 event.begin(); 186 event.end(); 187 event.commit(); 188 } 189 190 public static class RunnableEvent extends Event 191 { 192 private String runnableClassName; 193 getRunnableClassName()194 String getRunnableClassName() { 195 return this.runnableClassName; 196 } 197 setRunnableClassName(final String operationName)198 void setRunnableClassName(final String operationName) { 199 this.runnableClassName = operationName; 200 } 201 } 202 } 203 } 204