Skip to content

Commit 83d51f4

Browse files
committed
[GR-54737] Add support for layered thread locals.
PullRequest: graal/18044
2 parents 01f3be0 + 8aea207 commit 83d51f4

File tree

10 files changed

+428
-58
lines changed

10 files changed

+428
-58
lines changed

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/imagelayer/ImageLayerBuildingSupport.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,11 @@ private static ImageLayerBuildingSupport singleton() {
7373
return ImageSingletons.lookup(ImageLayerBuildingSupport.class);
7474
}
7575

76+
@Platforms(Platform.HOSTED_ONLY.class)
77+
public static boolean firstImageBuild() {
78+
return !buildingImageLayer() || buildingInitialLayer();
79+
}
80+
7681
@Platforms(Platform.HOSTED_ONLY.class)
7782
public static boolean lastImageBuild() {
7883
return !buildingImageLayer() || buildingApplicationLayer();

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/layeredimagesingleton/FeatureSingleton.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,10 @@
2727
import java.util.EnumSet;
2828

2929
/**
30-
* Feature singletons are hosted only and can only be accessed during build time.
30+
* Feature singletons are hosted only and can only be accessed during build time. Further, we
31+
* currently do not allow features to save information across layers.
3132
*/
32-
public interface FeatureSingleton extends LayeredImageSingleton {
33+
public interface FeatureSingleton extends UnsavedSingleton {
3334

3435
@Override
3536
default EnumSet<LayeredImageSingletonBuilderFlags> getImageBuilderFlags() {
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright (c) 2024, 2024, 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. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package com.oracle.svm.core.layeredimagesingleton;
26+
27+
import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport;
28+
import com.oracle.svm.core.util.VMError;
29+
30+
public interface InitialLayerOnlyImageSingleton extends LayeredImageSingleton {
31+
32+
@Override
33+
default PersistFlags preparePersist(ImageSingletonWriter writer) {
34+
VMError.guarantee(ImageLayerBuildingSupport.buildingInitialLayer(), "This singleton should only be installed in the initial layer");
35+
return PersistFlags.FORBIDDEN;
36+
}
37+
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/Safepoint.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@
6767
import com.oracle.svm.core.threadlocal.FastThreadLocal;
6868
import com.oracle.svm.core.threadlocal.FastThreadLocalFactory;
6969
import com.oracle.svm.core.threadlocal.FastThreadLocalInt;
70-
import com.oracle.svm.core.threadlocal.VMThreadLocalInfos;
70+
import com.oracle.svm.core.threadlocal.VMThreadLocalOffsetProvider;
7171
import com.oracle.svm.core.util.DuplicatedInNativeCode;
7272
import com.oracle.svm.core.util.TimeUtils;
7373
import com.oracle.svm.core.util.VMError;
@@ -405,7 +405,7 @@ public static LocationIdentity getThreadLocalSafepointRequestedLocationIdentity(
405405
}
406406

407407
public static int getThreadLocalSafepointRequestedOffset() {
408-
return VMThreadLocalInfos.getOffset(safepointRequested);
408+
return VMThreadLocalOffsetProvider.getOffset(safepointRequested);
409409
}
410410

411411
/**

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/threadlocal/VMThreadLocalInfos.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import static com.oracle.svm.core.Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE;
2828

2929
import java.util.Collection;
30+
import java.util.EnumSet;
3031

3132
import org.graalvm.nativeimage.ImageSingletons;
3233
import org.graalvm.nativeimage.IsolateThread;
@@ -42,12 +43,14 @@
4243
import com.oracle.svm.core.feature.AutomaticallyRegisteredImageSingleton;
4344
import com.oracle.svm.core.heap.ReferenceAccess;
4445
import com.oracle.svm.core.heap.UnknownObjectField;
46+
import com.oracle.svm.core.layeredimagesingleton.InitialLayerOnlyImageSingleton;
47+
import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonBuilderFlags;
4548
import com.oracle.svm.core.log.Log;
4649

4750
import jdk.graal.compiler.word.Word;
4851

4952
@AutomaticallyRegisteredImageSingleton
50-
public class VMThreadLocalInfos {
53+
public class VMThreadLocalInfos implements InitialLayerOnlyImageSingleton {
5154
/**
5255
* The {@link VMThreadLocalInfo} objects are scanned during analysis as soon as they are
5356
* discovered. After analysis, they are sorted and stored in the infos field.
@@ -127,4 +130,9 @@ public static int getOffset(FastThreadLocal threadLocal) {
127130
}
128131
return -1;
129132
}
133+
134+
@Override
135+
public EnumSet<LayeredImageSingletonBuilderFlags> getImageBuilderFlags() {
136+
return LayeredImageSingletonBuilderFlags.ALL_ACCESS;
137+
}
130138
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright (c) 2024, 2024, 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. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package com.oracle.svm.core.threadlocal;
26+
27+
import org.graalvm.nativeimage.ImageSingletons;
28+
29+
import com.oracle.svm.core.SubstrateUtil;
30+
import org.graalvm.nativeimage.Platform;
31+
import org.graalvm.nativeimage.Platforms;
32+
33+
public interface VMThreadLocalOffsetProvider {
34+
35+
static int getOffset(FastThreadLocal threadLocal) {
36+
if (SubstrateUtil.HOSTED) {
37+
return ImageSingletons.lookup(VMThreadLocalOffsetProvider.class).offsetOf(threadLocal);
38+
} else {
39+
return VMThreadLocalInfos.getOffset(threadLocal);
40+
}
41+
42+
}
43+
44+
@Platforms(Platform.HOSTED_ONLY.class)
45+
int offsetOf(FastThreadLocal threadLocal);
46+
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/threadlocal/VMThreadLocalSupport.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
*/
2525
package com.oracle.svm.core.threadlocal;
2626

27+
import java.util.EnumSet;
28+
2729
import org.graalvm.nativeimage.ImageSingletons;
2830
import org.graalvm.nativeimage.IsolateThread;
2931
import org.graalvm.nativeimage.Platform;
@@ -38,10 +40,12 @@
3840
import com.oracle.svm.core.heap.ObjectReferenceVisitor;
3941
import com.oracle.svm.core.heap.UnknownObjectField;
4042
import com.oracle.svm.core.heap.UnknownPrimitiveField;
43+
import com.oracle.svm.core.layeredimagesingleton.InitialLayerOnlyImageSingleton;
44+
import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonBuilderFlags;
4145

4246
import jdk.graal.compiler.api.replacements.Fold;
4347

44-
public class VMThreadLocalSupport {
48+
public class VMThreadLocalSupport implements InitialLayerOnlyImageSingleton {
4549
@UnknownPrimitiveField(availability = ReadyForCompilation.class) public int vmThreadSize = -1;
4650
@UnknownObjectField(availability = ReadyForCompilation.class) public byte[] vmThreadReferenceMapEncoding;
4751
@UnknownPrimitiveField(availability = ReadyForCompilation.class) public long vmThreadReferenceMapIndex = -1;
@@ -74,4 +78,9 @@ public void walk(IsolateThread isolateThread, ObjectReferenceVisitor referenceVi
7478
NonmovableArray<Byte> threadRefMapEncoding = NonmovableArrays.fromImageHeap(vmThreadReferenceMapEncoding);
7579
InstanceReferenceMapDecoder.walkOffsetsFromPointer((Pointer) isolateThread, threadRefMapEncoding, vmThreadReferenceMapIndex, referenceVisitor, null);
7680
}
81+
82+
@Override
83+
public EnumSet<LayeredImageSingletonBuilderFlags> getImageBuilderFlags() {
84+
return LayeredImageSingletonBuilderFlags.ALL_ACCESS;
85+
}
7786
}
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
/*
2+
* Copyright (c) 2024, 2024, 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. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package com.oracle.svm.hosted.thread;
26+
27+
import java.util.ArrayList;
28+
import java.util.HashMap;
29+
import java.util.Iterator;
30+
import java.util.List;
31+
import java.util.Map;
32+
33+
import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport;
34+
import com.oracle.svm.core.layeredimagesingleton.ImageSingletonLoader;
35+
import com.oracle.svm.core.layeredimagesingleton.ImageSingletonWriter;
36+
import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingleton;
37+
import com.oracle.svm.core.threadlocal.FastThreadLocal;
38+
import com.oracle.svm.core.threadlocal.VMThreadLocalInfo;
39+
import com.oracle.svm.core.threadlocal.VMThreadLocalInfos;
40+
import com.oracle.svm.core.threadlocal.VMThreadLocalSupport;
41+
import com.oracle.svm.core.util.VMError;
42+
43+
import jdk.graal.compiler.debug.Assertions;
44+
45+
/**
46+
* When building layered images we assume all used thread locals will be seen within the base layer.
47+
* Subsequent layers then need to be assigned the same offset. In addition, we match based on the
48+
* name assigned to ThreadLocals, so this must be unique.
49+
*
50+
* Note it is possible to relax this constraint of only allowing thread locals to be defined in the
51+
* initial layer. However, doing so will require adjusting {@link VMThreadLocalInfos} to be a
52+
* multi-layered singleton and also {@link VMThreadLocalSupport} to likely be an application layer
53+
* only image singleton.
54+
*/
55+
public class LayeredVMThreadLocalCollector extends VMThreadLocalCollector implements LayeredImageSingleton {
56+
57+
record ThreadInfo(int size, int offset) {
58+
59+
}
60+
61+
final Map<String, ThreadInfo> threadLocalAssignmentMap;
62+
private final boolean initialLayer;
63+
private int nextOffset;
64+
65+
public LayeredVMThreadLocalCollector() {
66+
this(null, -1);
67+
}
68+
69+
private LayeredVMThreadLocalCollector(Map<String, ThreadInfo> threadLocalAssignmentMap, int nextOffset) {
70+
super(true);
71+
72+
this.threadLocalAssignmentMap = threadLocalAssignmentMap;
73+
initialLayer = ImageLayerBuildingSupport.buildingInitialLayer();
74+
this.nextOffset = nextOffset;
75+
}
76+
77+
@Override
78+
public Object apply(Object source) {
79+
/*
80+
* Make sure all names have been assigned in the prior layers
81+
*/
82+
if (!initialLayer) {
83+
if (source instanceof FastThreadLocal threadLocal) {
84+
var name = threadLocal.getName();
85+
VMError.guarantee(threadLocalAssignmentMap.containsKey(name), "Found thread local which was not created in the initial layer %s", name);
86+
}
87+
}
88+
return super.apply(source);
89+
}
90+
91+
@Override
92+
public int sortAndAssignOffsets() {
93+
if (initialLayer) {
94+
assert nextOffset == -1 : nextOffset;
95+
96+
nextOffset = super.sortAndAssignOffsets();
97+
} else {
98+
assert nextOffset != -1;
99+
100+
for (VMThreadLocalInfo info : threadLocals.values()) {
101+
var assignment = threadLocalAssignmentMap.get(info.name);
102+
info.offset = assignment.offset();
103+
assert assignment.size() == calculateSize(info) : Assertions.errorMessage("Mismatch in computed size: ", assignment.size(), calculateSize(info), info.name);
104+
info.sizeInBytes = assignment.size();
105+
}
106+
}
107+
108+
return nextOffset;
109+
}
110+
111+
@Override
112+
public int getOffset(FastThreadLocal threadLocal) {
113+
if (initialLayer) {
114+
return super.getOffset(threadLocal);
115+
} else {
116+
return threadLocalAssignmentMap.get(threadLocal.getName()).offset();
117+
}
118+
}
119+
120+
@Override
121+
public PersistFlags preparePersist(ImageSingletonWriter writer) {
122+
/*
123+
* Store the (name, offset, size) tuple of all thread locals.
124+
*/
125+
List<String> threadLocalNames = new ArrayList<>();
126+
List<Integer> threadLocalOffsets = new ArrayList<>();
127+
List<Integer> threadLocalSizes = new ArrayList<>();
128+
if (initialLayer) {
129+
for (var threadLocal : getSortedThreadLocalInfos()) {
130+
threadLocalNames.add(threadLocal.name);
131+
threadLocalOffsets.add(threadLocal.offset);
132+
threadLocalSizes.add(threadLocal.sizeInBytes);
133+
}
134+
} else {
135+
for (var entry : threadLocalAssignmentMap.entrySet()) {
136+
threadLocalNames.add(entry.getKey());
137+
threadLocalOffsets.add(entry.getValue().offset());
138+
threadLocalSizes.add(entry.getValue().size());
139+
}
140+
}
141+
142+
writer.writeStringList("threadLocalNames", threadLocalNames);
143+
writer.writeIntList("threadLocalOffsets", threadLocalOffsets);
144+
writer.writeIntList("threadLocalSizes", threadLocalSizes);
145+
146+
/*
147+
* Note while it is not strictly necessary to store nextOffset at the moment, if in the
148+
* future we allow multiple layers to define thread locals then this information will need
149+
* to be propagated.
150+
*/
151+
writer.writeInt("nextOffset", nextOffset);
152+
return PersistFlags.CREATE;
153+
}
154+
155+
@SuppressWarnings("unused")
156+
public static Object createFromLoader(ImageSingletonLoader loader) {
157+
/*
158+
* Load the (name, offset, size) tuple of all thread locals.
159+
*/
160+
HashMap<String, ThreadInfo> threadLocalAssignmentMap = new HashMap<>();
161+
Iterator<String> threadLocalNames = loader.readStringList("threadLocalNames").iterator();
162+
Iterator<Integer> threadLocalOffsets = loader.readIntList("threadLocalOffsets").iterator();
163+
Iterator<Integer> threadLocalSizes = loader.readIntList("threadLocalSizes").iterator();
164+
165+
while (threadLocalNames.hasNext()) {
166+
String threadLocalName = threadLocalNames.next();
167+
int threadLocalOffset = threadLocalOffsets.next();
168+
int threadLocalSize = threadLocalSizes.next();
169+
170+
var previous = threadLocalAssignmentMap.put(threadLocalName, new ThreadInfo(threadLocalSize, threadLocalOffset));
171+
assert previous == null : previous;
172+
}
173+
174+
return new LayeredVMThreadLocalCollector(Map.copyOf(threadLocalAssignmentMap), loader.readInt("nextOffset"));
175+
}
176+
}

0 commit comments

Comments
 (0)