Skip to content

Commit a14140e

Browse files
author
Christian Wimmer
committed
Do not use secondary monitor map for locking on Class
1 parent 868d594 commit a14140e

File tree

4 files changed

+63
-68
lines changed

4 files changed

+63
-68
lines changed

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java

Lines changed: 19 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,6 @@
8888
public final class DynamicHub implements JavaKind.FormatWithToString, AnnotatedElement, java.lang.reflect.Type, GenericDeclaration, Serializable,
8989
Target_java_lang_invoke_TypeDescriptor_OfField, Target_java_lang_constant_Constable {
9090

91-
/** Marker value for {@link #classLoader}. */
92-
static final Object NO_CLASS_LOADER = new Object();
93-
9491
@Substitute //
9592
private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];
9693

@@ -272,11 +269,6 @@ public final class DynamicHub implements JavaKind.FormatWithToString, AnnotatedE
272269
*/
273270
private ClassInitializationInfo classInitializationInfo;
274271

275-
/**
276-
* Classloader used for loading this class during image-build time.
277-
*/
278-
private final Object classLoader;
279-
280272
/**
281273
* Array containing this type's type check id information. During a type check, a requested
282274
* column of this array is read to determine if this value fits within the range of ids which
@@ -305,7 +297,7 @@ public void setModule(Module module) {
305297
this.module = module;
306298
}
307299

308-
private final LazyFinalReference<DynamicHubCompanion> companion = new LazyFinalReference<>(() -> new DynamicHubCompanion(this));
300+
private final DynamicHubCompanion companion;
309301

310302
@Platforms(Platform.HOSTED_ONLY.class)
311303
public DynamicHub(Class<?> hostedJavaClass, String name, HubType hubType, ReferenceType referenceType, Object isLocalClass, Object isAnonymousClass, DynamicHub superType, DynamicHub componentHub,
@@ -321,7 +313,6 @@ public DynamicHub(Class<?> hostedJavaClass, String name, HubType hubType, Refere
321313
this.componentType = componentHub;
322314
this.sourceFileName = sourceFileName;
323315
this.modifiers = modifiers;
324-
this.classLoader = PredefinedClassesSupport.isPredefined(hostedJavaClass) ? NO_CLASS_LOADER : classLoader;
325316
this.nestHost = nestHost;
326317

327318
this.flags = NumUtil.safeToUByte(makeFlag(IS_PRIMITIVE_FLAG_BIT, hostedJavaClass.isPrimitive()) |
@@ -332,6 +323,8 @@ public DynamicHub(Class<?> hostedJavaClass, String name, HubType hubType, Refere
332323
makeFlag(HAS_DEFAULT_METHODS_FLAG_BIT, hasDefaultMethods) |
333324
makeFlag(DECLARES_DEFAULT_METHODS_FLAG_BIT, declaresDefaultMethods) |
334325
makeFlag(IS_SEALED_FLAG_BIT, isSealed));
326+
327+
this.companion = new DynamicHubCompanion(hostedJavaClass, classLoader);
335328
}
336329

337330
@Platforms(Platform.HOSTED_ONLY.class)
@@ -560,6 +553,10 @@ public static DynamicHub fromClass(Class<?> clazz) {
560553
return SubstrateUtil.cast(clazz, DynamicHub.class);
561554
}
562555

556+
public DynamicHubCompanion getCompanion() {
557+
return companion;
558+
}
559+
563560
/*
564561
* Note that this method must be a static method and not an instance method, otherwise null
565562
* values cannot be converted.
@@ -696,18 +693,15 @@ public InputStream getResourceAsStream(String resourceName) {
696693

697694
@Substitute
698695
private ClassLoader getClassLoader0() {
699-
if (classLoader == NO_CLASS_LOADER) {
700-
return companion.get().getClassLoader();
701-
}
702-
return (ClassLoader) classLoader;
696+
return companion.getClassLoader();
703697
}
704698

705699
public boolean isLoaded() {
706-
return classLoader != NO_CLASS_LOADER || (companion.isPresent() && companion.get().hasClassLoader());
700+
return companion.hasClassLoader();
707701
}
708702

709703
void setClassLoaderAtRuntime(ClassLoader loader) {
710-
companion.get().setClassLoader(loader);
704+
companion.setClassLoader(loader);
711705
}
712706

713707
@Substitute
@@ -1066,7 +1060,7 @@ private Method getMethod(@SuppressWarnings("hiding") String name, Class<?>... pa
10661060
* The original code of getMethods() does a recursive search to avoid creating objects for
10671061
* all public methods. We prepare them during the image build and can just iterate here.
10681062
*/
1069-
Method method = searchMethods(companion.get().getCompleteReflectionData().publicMethods, name, parameterTypes);
1063+
Method method = searchMethods(companion.getCompleteReflectionData(this).publicMethods, name, parameterTypes);
10701064
if (method == null) {
10711065
throw new NoSuchMethodException(describeMethod(getName() + "." + name + "(", parameterTypes, ")"));
10721066
}
@@ -1112,7 +1106,8 @@ private Class<?>[] getClasses() {
11121106

11131107
@Substitute
11141108
private Constructor<?>[] privateGetDeclaredConstructors(boolean publicOnly) {
1115-
return publicOnly ? companion.get().getCompleteReflectionData().publicConstructors : companion.get().getCompleteReflectionData().declaredConstructors;
1109+
ReflectionData reflectionData = companion.getCompleteReflectionData(this);
1110+
return publicOnly ? reflectionData.publicConstructors : reflectionData.declaredConstructors;
11161111
}
11171112

11181113
@Substitute
@@ -1122,7 +1117,8 @@ private Field[] privateGetDeclaredFields(boolean publicOnly) {
11221117

11231118
@Substitute
11241119
private Method[] privateGetDeclaredMethods(boolean publicOnly) {
1125-
return publicOnly ? companion.get().getCompleteReflectionData().declaredPublicMethods : companion.get().getCompleteReflectionData().declaredMethods;
1120+
ReflectionData reflectionData = companion.getCompleteReflectionData(this);
1121+
return publicOnly ? reflectionData.declaredPublicMethods : reflectionData.declaredMethods;
11261122
}
11271123

11281124
@Substitute
@@ -1132,7 +1128,7 @@ private Field[] privateGetPublicFields() {
11321128

11331129
@Substitute
11341130
Method[] privateGetPublicMethods() {
1135-
return companion.get().getCompleteReflectionData().publicMethods;
1131+
return companion.getCompleteReflectionData(this).publicMethods;
11361132
}
11371133

11381134
@KeepOriginal
@@ -1292,7 +1288,7 @@ public String getPackageName() {
12921288
if (SubstrateUtil.HOSTED) { // avoid eager initialization in image heap
12931289
return computePackageName();
12941290
}
1295-
return companion.get().getPackageName();
1291+
return companion.getPackageName(this);
12961292
}
12971293

12981294
String computePackageName() {
@@ -1330,7 +1326,7 @@ public Object[] getSigners() {
13301326

13311327
@Substitute
13321328
public ProtectionDomain getProtectionDomain() {
1333-
return companion.get().getProtectionDomain();
1329+
return companion.getProtectionDomain();
13341330
}
13351331

13361332
@TargetElement(onlyWith = JDK17OrLater.class)
@@ -1340,7 +1336,7 @@ private ProtectionDomain protectionDomain() {
13401336
}
13411337

13421338
void setProtectionDomainAtRuntime(ProtectionDomain protectionDomain) {
1343-
companion.get().setProtectionDomain(protectionDomain);
1339+
companion.setProtectionDomain(protectionDomain);
13441340
}
13451341

13461342
@Substitute

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHubCompanion.java

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@
2626

2727
// Checkstyle: allow reflection
2828

29-
import static com.oracle.svm.core.hub.DynamicHub.NO_CLASS_LOADER;
30-
3129
import java.lang.reflect.Constructor;
3230
import java.lang.reflect.Executable;
3331
import java.lang.reflect.Method;
@@ -39,27 +37,38 @@
3937

4038
import org.graalvm.collections.Pair;
4139
import org.graalvm.nativeimage.ImageSingletons;
40+
import org.graalvm.nativeimage.Platform;
41+
import org.graalvm.nativeimage.Platforms;
4242

4343
import com.oracle.svm.core.hub.DynamicHub.ReflectionData;
4444
import com.oracle.svm.core.jdk.ProtectionDomainSupport;
4545
import com.oracle.svm.core.reflect.MethodMetadataDecoder;
4646
import com.oracle.svm.core.reflect.MethodMetadataDecoder.MethodDescriptor;
4747
import com.oracle.svm.core.util.VMError;
4848

49-
/** An optional, non-immutable companion to a {@link DynamicHub} instance. */
49+
/**
50+
* The mutable parts of a {@link DynamicHub} instance.
51+
*/
5052
public final class DynamicHubCompanion {
51-
private final DynamicHub hub;
53+
/** Marker value for {@link #classLoader}. */
54+
private static final Object NO_CLASS_LOADER = new Object();
5255

5356
private String packageName;
54-
private Object classLoader = NO_CLASS_LOADER;
57+
/**
58+
* Classloader used for loading this class. Most classes have the correct class loader set
59+
* already at image build time. {@link PredefinedClassesSupport Predefined classes} get their
60+
* classloader only at run time, before "loading" the field value is {@link #NO_CLASS_LOADER}.
61+
*/
62+
private Object classLoader;
5563
private ProtectionDomain protectionDomain;
5664
private ReflectionData completeReflectionData;
5765

58-
public DynamicHubCompanion(DynamicHub hub) {
59-
this.hub = hub;
66+
@Platforms(Platform.HOSTED_ONLY.class)
67+
DynamicHubCompanion(Class<?> hostedJavaClass, ClassLoader classLoader) {
68+
this.classLoader = PredefinedClassesSupport.isPredefined(hostedJavaClass) ? NO_CLASS_LOADER : classLoader;
6069
}
6170

62-
public String getPackageName() {
71+
String getPackageName(DynamicHub hub) {
6372
if (packageName == null) {
6473
packageName = hub.computePackageName();
6574
}
@@ -70,30 +79,30 @@ boolean hasClassLoader() {
7079
return classLoader != NO_CLASS_LOADER;
7180
}
7281

73-
public ClassLoader getClassLoader() {
82+
ClassLoader getClassLoader() {
7483
Object loader = classLoader;
7584
VMError.guarantee(loader != NO_CLASS_LOADER);
7685
return (ClassLoader) loader;
7786
}
7887

79-
public void setClassLoader(ClassLoader loader) {
88+
void setClassLoader(ClassLoader loader) {
8089
VMError.guarantee(classLoader == NO_CLASS_LOADER && loader != NO_CLASS_LOADER);
8190
classLoader = loader;
8291
}
8392

84-
public ProtectionDomain getProtectionDomain() {
93+
ProtectionDomain getProtectionDomain() {
8594
if (protectionDomain == null) {
8695
protectionDomain = ProtectionDomainSupport.allPermDomain();
8796
}
8897
return protectionDomain;
8998
}
9099

91-
public void setProtectionDomain(ProtectionDomain domain) {
100+
void setProtectionDomain(ProtectionDomain domain) {
92101
VMError.guarantee(protectionDomain == null && domain != null);
93102
protectionDomain = domain;
94103
}
95104

96-
public ReflectionData getCompleteReflectionData() {
105+
ReflectionData getCompleteReflectionData(DynamicHub hub) {
97106
if (completeReflectionData == null) {
98107
List<Method> newDeclaredMethods = new ArrayList<>(Arrays.asList(hub.rd.declaredMethods));
99108
List<Method> newPublicMethods = new ArrayList<>(Arrays.asList(hub.rd.publicMethods));

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/MonitorSupport.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,10 @@
2626

2727
import org.graalvm.compiler.api.replacements.Fold;
2828
import org.graalvm.nativeimage.ImageSingletons;
29+
import org.graalvm.nativeimage.IsolateThread;
2930

3031
import com.oracle.svm.core.annotate.Uninterruptible;
31-
import org.graalvm.nativeimage.IsolateThread;
32+
import com.oracle.svm.core.hub.DynamicHub;
3233

3334
/**
3435
* This interface provides functions related to monitor operations (the Java "synchronized" keyword
@@ -51,6 +52,18 @@ public static MonitorSupport singleton() {
5152
*/
5253
public abstract void monitorExit(Object obj);
5354

55+
public Object replaceObject(Object unreplacedObject) {
56+
if (unreplacedObject instanceof DynamicHub) {
57+
/*
58+
* Classes (= DynamicHub) never have a monitor slot because they must be immutable.
59+
* Since the companion object is never exposed to user code, we can use it as a
60+
* replacement object that is mutable and is marked to always have a monitor slot.
61+
*/
62+
return ((DynamicHub) unreplacedObject).getCompanion();
63+
}
64+
return unreplacedObject;
65+
}
66+
5467
/*
5568
* Support for objects that are re-locked during deoptimization. This method is called when
5669
* preparing the {@link DeoptimizedFrame}. This is done in a VM operation and can therefore run

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/MultiThreadedMonitorSupport.java

Lines changed: 8 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
import com.oracle.svm.core.annotate.TargetClass;
5858
import com.oracle.svm.core.annotate.Uninterruptible;
5959
import com.oracle.svm.core.hub.DynamicHub;
60+
import com.oracle.svm.core.hub.DynamicHubCompanion;
6061
import com.oracle.svm.core.snippets.SubstrateForeignCallTarget;
6162
import com.oracle.svm.core.stack.StackOverflowCheck;
6263
import com.oracle.svm.core.thread.JavaContinuations;
@@ -174,6 +175,12 @@ protected static void onMonitorUnlocked() {
174175
monitorTypes.add(Class.forName("jdk.internal.ref.PhantomCleanable"));
175176
}
176177

178+
/*
179+
* Use as the delegate for locking on {@link Class} (i.e. {@link DynamicHub}) since the
180+
* hub itself must be immutable.
181+
*/
182+
monitorTypes.add(DynamicHubCompanion.class);
183+
177184
FORCE_MONITOR_SLOT_TYPES = Collections.unmodifiableSet(monitorTypes);
178185
} catch (ClassNotFoundException e) {
179186
throw VMError.shouldNotReachHere("Error building the list of types that always need a monitor slot.", e);
@@ -422,38 +429,8 @@ protected static int getMonitorOffset(Object obj) {
422429
return DynamicHub.fromClass(obj.getClass()).getMonitorOffset();
423430
}
424431

425-
private static final Object CLEANER_CLASS;
426-
private static final Object CLEANER_REPLACEMENT;
427-
428-
static {
429-
try {
430-
CLEANER_CLASS = Class.forName("jdk.internal.ref.Cleaner");
431-
} catch (ClassNotFoundException ex) {
432-
throw VMError.shouldNotReachHere(ex);
433-
}
434-
CLEANER_REPLACEMENT = new Object();
435-
VMError.guarantee(FORCE_MONITOR_SLOT_TYPES.contains(CLEANER_REPLACEMENT.getClass()), "Must have a monitor slot for Cleaner replacement object");
436-
}
437-
438432
protected final ReentrantLock getOrCreateMonitor(Object unreplacedObject, boolean createIfNotExisting) {
439-
Object obj;
440-
if (unreplacedObject == CLEANER_CLASS) {
441-
/*
442-
* Workaround for jdk.internal.ref.Cleaner when cleaners do not run in a separate
443-
* thread. Cleaner uses static synchronized methods. Since classes (= DynamicHub) never
444-
* have a monitor slot, static synchronized methods always use the additionalMonitors
445-
* map. When a Cleaner then runs at a time where the application thread already holds
446-
* the additionalMonitorsLock, i.e., when a GC runs while allocating a monitor in
447-
* getOrCreateMonitorFromMap(), a disallowed recursive locking of additionalMonitorsLock
448-
* would happen. Note that CLEANER_REPLACEMENT is an Object which always has a monitor
449-
* slot. This workaround will be removed by GR-35898 when we have a better
450-
* implementation of static synchronized methods.
451-
*/
452-
obj = CLEANER_REPLACEMENT;
453-
} else {
454-
obj = unreplacedObject;
455-
}
456-
433+
Object obj = replaceObject(unreplacedObject);
457434
assert obj != null;
458435
int monitorOffset = getMonitorOffset(obj);
459436
if (monitorOffset != 0) {

0 commit comments

Comments
 (0)