--- old/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java 2015-11-05 17:15:53.606035239 +0100 +++ new/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java 2015-11-05 17:15:53.549034930 +0100 @@ -34,7 +34,9 @@ import java.lang.invoke.MethodHandles.Lookup; import java.lang.reflect.Field; import java.util.Arrays; -import java.util.HashMap; +import java.util.function.Function; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ConcurrentHashMap; import sun.invoke.util.ValueConversions; import sun.invoke.util.Wrapper; @@ -355,26 +357,14 @@ assert(!INIT_DONE); if (constructor() == null) { String types = typeChars; + CACHE.put(types, this); Factory.makeCtors(clazz, types, this.constructor); Factory.makeGetters(clazz, types, this.getters); Factory.makeNominalGetters(types, this.nominalGetters, this.getters); } } - private SpeciesData(String typeChars) { - // Placeholder only. - this.typeChars = typeChars; - this.typeCodes = basicTypes(typeChars); - this.clazz = null; - this.constructor = null; - this.getters = null; - this.nominalGetters = null; - this.extensions = null; - } - private boolean isPlaceholder() { return clazz == null; } - - private static final HashMap CACHE = new HashMap<>(); - static { CACHE.put("", EMPTY); } // make bootstrap predictable + private static final ConcurrentMap CACHE = new ConcurrentHashMap<>(); private static final boolean INIT_DONE; // set after finishes... SpeciesData extendWith(byte type) { @@ -390,45 +380,31 @@ } private static SpeciesData get(String types) { - // Acquire cache lock for query. - SpeciesData d = lookupCache(types); - if (!d.isPlaceholder()) - return d; - synchronized (d) { - // Use synch. on the placeholder to prevent multiple instantiation of one species. - // Creating this class forces a recursive call to getForClass. - if (lookupCache(types).isPlaceholder()) - Factory.generateConcreteBMHClass(types); - } - // Reacquire cache lock. - d = lookupCache(types); - // Class loading must have upgraded the cache. - assert(d != null && !d.isPlaceholder()); + SpeciesData d = CACHE.get(types); + if (d == null) { + Class bmhcl = Factory.getConcreteBMHClass(types); + // obtain value of SPECIES_DATA static field. + // This triggers class initialization which might fail! + d = Factory.speciesDataFromConcreteBMHClass(bmhcl); + SpeciesData d2 = CACHE.putIfAbsent(types, d); + assert d2 == null || d2 == d; + } return d; } + + // a call-back from generated BMH subclass' static SpeciesData getForClass(String types, Class clazz) { // clazz is a new class which is initializing its SPECIES_DATA field - return updateCache(types, new SpeciesData(types, clazz)); - } - private static synchronized SpeciesData lookupCache(String types) { - SpeciesData d = CACHE.get(types); - if (d != null) return d; - d = new SpeciesData(types); - assert(d.isPlaceholder()); - CACHE.put(types, d); - return d; - } - private static synchronized SpeciesData updateCache(String types, SpeciesData d) { - SpeciesData d2; - assert((d2 = CACHE.get(types)) == null || d2.isPlaceholder()); - assert(!d.isPlaceholder()); - CACHE.put(types, d); - return d; + return new SpeciesData(types, clazz); } - static { - // pre-fill the BMH speciesdata cache with BMH's inner classes - final Class rootCls = BoundMethodHandle.class; + /** + * This is to be called when assertions are enabled. It checks whether SpeciesData for all of the statically + * defined species subclasses of BoundMethodHandle has been added to the SpeciesData cache. See below in the + * static initializer for + */ + static boolean speciesDataCachePopulated() { + Class rootCls = BoundMethodHandle.class; try { for (Class c : rootCls.getDeclaredClasses()) { if (rootCls.isAssignableFrom(c)) { @@ -436,16 +412,21 @@ SpeciesData d = Factory.speciesDataFromConcreteBMHClass(cbmh); assert(d != null) : cbmh.getName(); assert(d.clazz == cbmh); - assert(d == lookupCache(d.typeChars)); + assert(CACHE.get(d.typeChars) == d); } } } catch (Throwable e) { throw newInternalError(e); } + return true; + } - for (SpeciesData d : CACHE.values()) { - d.initForBootstrap(); - } + static { + // Pre-fill the BMH species-data cache with EMPTY and all BMH's inner subclasses. + EMPTY.initForBootstrap(); + Species_L.SPECIES_DATA.initForBootstrap(); + // check that all static SpeciesData instances have been initialized + assert speciesDataCachePopulated(); // Note: Do not simplify this, because INIT_DONE must not be // a compile-time constant during bootstrapping. INIT_DONE = Boolean.TRUE; @@ -493,6 +474,26 @@ static final String[] E_THROWABLE = new String[] { "java/lang/Throwable" }; + static final ConcurrentMap> CLASS_CACHE = new ConcurrentHashMap<>(); + + /** + * Get a concrete subclass of BMH for a given combination of bound types. + * + * @param types the type signature, wherein reference types are erased to 'L' + * @return the concrete BMH class + */ + static Class getConcreteBMHClass(String types) { + // CHM.computeIfAbsent ensures generateConcreteBMHClass is called + // only once per key. + return CLASS_CACHE.computeIfAbsent( + types, new Function>() { + @Override + public Class apply(String types) { + return generateConcreteBMHClass(types); + } + }); + } + /** * Generate a concrete subclass of BMH for a given combination of bound types. * @@ -715,7 +716,6 @@ UNSAFE.defineClass(className, classFile, 0, classFile.length, BoundMethodHandle.class.getClassLoader(), null) .asSubclass(BoundMethodHandle.class); - UNSAFE.ensureClassInitialized(bmhClass); return bmhClass; }