--- old/src/java.base/share/classes/java/lang/invoke/ClassSpecializer.java 2018-04-25 18:29:09.285099325 +0200 +++ new/src/java.base/share/classes/java/lang/invoke/ClassSpecializer.java 2018-04-25 18:29:09.207098857 +0200 @@ -38,8 +38,6 @@ import java.security.ProtectionDomain; import java.util.*; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.function.Function; import static java.lang.invoke.LambdaForm.*; import static java.lang.invoke.MethodHandleNatives.Constants.REF_getStatic; @@ -64,7 +62,7 @@ private final List transformMethods; private final MethodType baseConstructorType; private final S topSpecies; - private final ConcurrentMap cache = new ConcurrentHashMap<>(); + private final ConcurrentHashMap cache = new ConcurrentHashMap<>(); private final Factory factory; private @Stable boolean topClassIsSuper; @@ -113,8 +111,7 @@ this.keyType = keyType; this.metaType = metaType; this.sdAccessor = sdAccessor; - // FIXME: use List.copyOf once 8177290 is in - this.transformMethods = List.of(transformMethods.toArray(new MemberName[transformMethods.size()])); + this.transformMethods = List.copyOf(transformMethods); this.sdFieldName = sdFieldName; this.baseConstructorType = baseConstructorType.changeReturnType(void.class); this.factory = makeFactory(); @@ -148,18 +145,13 @@ return new IllegalArgumentException(message, cause); } + @SuppressWarnings("unchecked") public final S findSpecies(K key) { - S speciesData = cache.computeIfAbsent(key, new Function<>() { - @Override - public S apply(K key1) { - return factory.loadSpecies(newSpeciesData(key1)); - } - }); // Note: Species instantiation may throw VirtualMachineError because of // code cache overflow. If this happens the species bytecode may be // loaded but not linked to its species metadata (with MH's etc). - // That will cause a throw out of CHM.computeIfAbsent, - // which will shut down the caller thread. + // That will cause a throw out of this method with a reservation object + // installed in the cache. // // In a latter attempt to get the same species, the already-loaded // class will be present in the system dictionary, causing an @@ -171,12 +163,44 @@ // (As an alternative, we might spin a new class with a new name, // or use the anonymous class mechanism.) // - // In the end, as long as everybody goes through the same CHM, - // CHM.computeIfAbsent will ensure only one SpeciesData will be set + // In the end, as long as everybody goes through this method, + // its initialization logic will ensure only one SpeciesData will be set // successfully on a concrete class if ever. // The concrete class is published via SpeciesData instance // returned here only after the class and species data are linked together. - assert(speciesData != null); + Object reservation = new Object(); + S speciesData; + Object speciesDataOrReservation = cache.putIfAbsent(key, reservation); + if (speciesDataOrReservation instanceof ClassSpecializer.SpeciesData) { + // already cached + speciesData = (S) speciesDataOrReservation; + } else { + // resolve the race for reservation + if (speciesDataOrReservation != null) { + reservation = speciesDataOrReservation; + } + synchronized (reservation) { + // re-check under lock + speciesDataOrReservation = cache.get(key); + if (speciesDataOrReservation == reservation) { + // the thread that enters synchronized (reservation) and finds + // reservation still in cache, tries to (re)initialize species data. + // if the initialization fails, the reservation object is left in CHM + // for next thread to pick it up and retry... + speciesData = factory.loadSpecies(newSpeciesData(key)); + if (!cache.replace(key, reservation, speciesData)) { + throw new AssertionError("Thought this was unreachable"); + } + } else { + // the thread that enters synchronized (reservation) and finds + // a reference that is not a reservation, has found the initialized + // species data... + assert speciesDataOrReservation instanceof ClassSpecializer.SpeciesData; + speciesData = (S) speciesDataOrReservation; + } + } + } + assert(speciesData != null && speciesData.isResolved()); return speciesData; } @@ -208,9 +232,7 @@ protected SpeciesData(K key) { this.key = keyType.cast(Objects.requireNonNull(key)); List> types = deriveFieldTypes(key); - // TODO: List.copyOf - int arity = types.size(); - this.fieldTypes = List.of(types.toArray(new Class[arity])); + this.fieldTypes = List.copyOf(types); } public final K key() { @@ -458,8 +480,8 @@ final Class speciesCode; if (salvage != null) { speciesCode = salvage.asSubclass(topClass()); - factory.linkSpeciesDataToCode(speciesData, speciesCode); - factory.linkCodeToSpeciesData(speciesCode, speciesData, true); + linkSpeciesDataToCode(speciesData, speciesCode); + linkCodeToSpeciesData(speciesCode, speciesData, true); } else { // Not pregenerated, generate the class try { @@ -486,7 +508,7 @@ if (!speciesData.isResolved()) { throw newInternalError("bad species class linkage for " + className + ": " + speciesData); } - assert(speciesData == factory.loadSpeciesDataFromCode(speciesCode)); + assert(speciesData == loadSpeciesDataFromCode(speciesCode)); return speciesData; }