8202548: Use reservation Object when creating SpeciesData

Co-authored-by: Peter Levart <peter.levart@gmail.com>
Reviewed-by: psandoz, plevart
This commit is contained in:
Claes Redestad 2018-05-08 10:49:59 +02:00
parent 035d0190a3
commit b329be2b75

@ -68,7 +68,7 @@ abstract class ClassSpecializer<T,K,S extends ClassSpecializer<T,K,S>.SpeciesDat
private final List<MemberName> transformMethods;
private final MethodType baseConstructorType;
private final S topSpecies;
private final ConcurrentHashMap<K, S> cache = new ConcurrentHashMap<>();
private final ConcurrentHashMap<K, Object> cache = new ConcurrentHashMap<>();
private final Factory factory;
private @Stable boolean topClassIsSuper;
@ -151,14 +151,20 @@ abstract class ClassSpecializer<T,K,S extends ClassSpecializer<T,K,S>.SpeciesDat
return new IllegalArgumentException(message, cause);
}
private static final Function<Object, Object> CREATE_RESERVATION = new Function<>() {
@Override
public Object apply(Object key) {
return new Object();
}
};
public final S findSpecies(K key) {
// 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 Factory.loadSpecies.
//
// In a latter attempt to get the same species, the already-loaded
// In a later attempt to get the same species, the already-loaded
// class will be present in the system dictionary, causing an
// error when the species generator tries to reload it.
// We try to detect this case and link the pre-existing code.
@ -168,25 +174,20 @@ abstract class ClassSpecializer<T,K,S extends ClassSpecializer<T,K,S>.SpeciesDat
// (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
// successfully on a concrete class if ever.
// In the end, as long as everybody goes through this findSpecies method,
// it 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.
S speciesData = cache.computeIfAbsent(key, new Function<>() {
@Override
public S apply(K key1) {
return newSpeciesData(key1);
}
});
Object speciesDataOrReservation = cache.computeIfAbsent(key, CREATE_RESERVATION);
// Separating the creation of a placeholder SpeciesData instance above
// from the loading and linking a real one below ensures we can never
// accidentally call computeIfAbsent recursively. Replacing rather than
// updating the placeholder is done to ensure safe publication.
if (!speciesData.isResolved()) {
synchronized (speciesData) {
S existingSpeciesData = cache.get(key);
if (existingSpeciesData == speciesData) { // won the race
// accidentally call computeIfAbsent recursively.
S speciesData;
if (speciesDataOrReservation.getClass() == Object.class) {
synchronized (speciesDataOrReservation) {
Object existingSpeciesData = cache.get(key);
if (existingSpeciesData == speciesDataOrReservation) { // won the race
// create a new SpeciesData...
speciesData = newSpeciesData(key);
// load and link it...
@ -195,9 +196,11 @@ abstract class ClassSpecializer<T,K,S extends ClassSpecializer<T,K,S>.SpeciesDat
throw newInternalError("Concurrent loadSpecies");
}
} else { // lost the race; the retrieved existingSpeciesData is the final
speciesData = existingSpeciesData;
speciesData = metaType.cast(existingSpeciesData);
}
}
} else {
speciesData = metaType.cast(speciesDataOrReservation);
}
assert(speciesData != null && speciesData.isResolved());
return speciesData;