Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ private static ImageLayerBuildingSupport singleton() {
return ImageSingletons.lookup(ImageLayerBuildingSupport.class);
}

@Platforms(Platform.HOSTED_ONLY.class)
public static boolean firstImageBuild() {
return !buildingImageLayer() || buildingInitialLayer();
}

@Platforms(Platform.HOSTED_ONLY.class)
public static boolean lastImageBuild() {
return !buildingImageLayer() || buildingApplicationLayer();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,10 @@
import java.util.EnumSet;

/**
* Feature singletons are hosted only and can only be accessed during build time.
* Feature singletons are hosted only and can only be accessed during build time. Further, we
* currently do not allow features to save information across layers.
*/
public interface FeatureSingleton extends LayeredImageSingleton {
public interface FeatureSingleton extends UnsavedSingleton {

@Override
default EnumSet<LayeredImageSingletonBuilderFlags> getImageBuilderFlags() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.oracle.svm.core.layeredimagesingleton;

import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport;
import com.oracle.svm.core.util.VMError;

public interface InitialLayerOnlyImageSingleton extends LayeredImageSingleton {

@Override
default PersistFlags preparePersist(ImageSingletonWriter writer) {
VMError.guarantee(ImageLayerBuildingSupport.buildingInitialLayer(), "This singleton should only be installed in the initial layer");
return PersistFlags.FORBIDDEN;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
import com.oracle.svm.core.threadlocal.FastThreadLocal;
import com.oracle.svm.core.threadlocal.FastThreadLocalFactory;
import com.oracle.svm.core.threadlocal.FastThreadLocalInt;
import com.oracle.svm.core.threadlocal.VMThreadLocalInfos;
import com.oracle.svm.core.threadlocal.VMThreadLocalOffsetProvider;
import com.oracle.svm.core.util.DuplicatedInNativeCode;
import com.oracle.svm.core.util.TimeUtils;
import com.oracle.svm.core.util.VMError;
Expand Down Expand Up @@ -405,7 +405,7 @@ public static LocationIdentity getThreadLocalSafepointRequestedLocationIdentity(
}

public static int getThreadLocalSafepointRequestedOffset() {
return VMThreadLocalInfos.getOffset(safepointRequested);
return VMThreadLocalOffsetProvider.getOffset(safepointRequested);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import static com.oracle.svm.core.Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE;

import java.util.Collection;
import java.util.EnumSet;

import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.IsolateThread;
Expand All @@ -42,12 +43,14 @@
import com.oracle.svm.core.feature.AutomaticallyRegisteredImageSingleton;
import com.oracle.svm.core.heap.ReferenceAccess;
import com.oracle.svm.core.heap.UnknownObjectField;
import com.oracle.svm.core.layeredimagesingleton.InitialLayerOnlyImageSingleton;
import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonBuilderFlags;
import com.oracle.svm.core.log.Log;

import jdk.graal.compiler.word.Word;

@AutomaticallyRegisteredImageSingleton
public class VMThreadLocalInfos {
public class VMThreadLocalInfos implements InitialLayerOnlyImageSingleton {
/**
* The {@link VMThreadLocalInfo} objects are scanned during analysis as soon as they are
* discovered. After analysis, they are sorted and stored in the infos field.
Expand Down Expand Up @@ -127,4 +130,9 @@ public static int getOffset(FastThreadLocal threadLocal) {
}
return -1;
}

@Override
public EnumSet<LayeredImageSingletonBuilderFlags> getImageBuilderFlags() {
return LayeredImageSingletonBuilderFlags.ALL_ACCESS;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.oracle.svm.core.threadlocal;

import org.graalvm.nativeimage.ImageSingletons;

import com.oracle.svm.core.SubstrateUtil;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;

public interface VMThreadLocalOffsetProvider {

static int getOffset(FastThreadLocal threadLocal) {
if (SubstrateUtil.HOSTED) {
return ImageSingletons.lookup(VMThreadLocalOffsetProvider.class).offsetOf(threadLocal);
} else {
return VMThreadLocalInfos.getOffset(threadLocal);
}

}

@Platforms(Platform.HOSTED_ONLY.class)
int offsetOf(FastThreadLocal threadLocal);
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
*/
package com.oracle.svm.core.threadlocal;

import java.util.EnumSet;

import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.IsolateThread;
import org.graalvm.nativeimage.Platform;
Expand All @@ -38,10 +40,12 @@
import com.oracle.svm.core.heap.ObjectReferenceVisitor;
import com.oracle.svm.core.heap.UnknownObjectField;
import com.oracle.svm.core.heap.UnknownPrimitiveField;
import com.oracle.svm.core.layeredimagesingleton.InitialLayerOnlyImageSingleton;
import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonBuilderFlags;

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

public class VMThreadLocalSupport {
public class VMThreadLocalSupport implements InitialLayerOnlyImageSingleton {
@UnknownPrimitiveField(availability = ReadyForCompilation.class) public int vmThreadSize = -1;
@UnknownObjectField(availability = ReadyForCompilation.class) public byte[] vmThreadReferenceMapEncoding;
@UnknownPrimitiveField(availability = ReadyForCompilation.class) public long vmThreadReferenceMapIndex = -1;
Expand Down Expand Up @@ -74,4 +78,9 @@ public void walk(IsolateThread isolateThread, ObjectReferenceVisitor referenceVi
NonmovableArray<Byte> threadRefMapEncoding = NonmovableArrays.fromImageHeap(vmThreadReferenceMapEncoding);
InstanceReferenceMapDecoder.walkOffsetsFromPointer((Pointer) isolateThread, threadRefMapEncoding, vmThreadReferenceMapIndex, referenceVisitor, null);
}

@Override
public EnumSet<LayeredImageSingletonBuilderFlags> getImageBuilderFlags() {
return LayeredImageSingletonBuilderFlags.ALL_ACCESS;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
/*
* Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.oracle.svm.hosted.thread;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport;
import com.oracle.svm.core.layeredimagesingleton.ImageSingletonLoader;
import com.oracle.svm.core.layeredimagesingleton.ImageSingletonWriter;
import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingleton;
import com.oracle.svm.core.threadlocal.FastThreadLocal;
import com.oracle.svm.core.threadlocal.VMThreadLocalInfo;
import com.oracle.svm.core.threadlocal.VMThreadLocalInfos;
import com.oracle.svm.core.threadlocal.VMThreadLocalSupport;
import com.oracle.svm.core.util.VMError;

import jdk.graal.compiler.debug.Assertions;

/**
* When building layered images we assume all used thread locals will be seen within the base layer.
* Subsequent layers then need to be assigned the same offset. In addition, we match based on the
* name assigned to ThreadLocals, so this must be unique.
*
* Note it is possible to relax this constraint of only allowing thread locals to be defined in the
* initial layer. However, doing so will require adjusting {@link VMThreadLocalInfos} to be a
* multi-layered singleton and also {@link VMThreadLocalSupport} to likely be an application layer
* only image singleton.
*/
public class LayeredVMThreadLocalCollector extends VMThreadLocalCollector implements LayeredImageSingleton {

record ThreadInfo(int size, int offset) {

}

final Map<String, ThreadInfo> threadLocalAssignmentMap;
private final boolean initialLayer;
private int nextOffset;

public LayeredVMThreadLocalCollector() {
this(null, -1);
}

private LayeredVMThreadLocalCollector(Map<String, ThreadInfo> threadLocalAssignmentMap, int nextOffset) {
super(true);

this.threadLocalAssignmentMap = threadLocalAssignmentMap;
initialLayer = ImageLayerBuildingSupport.buildingInitialLayer();
this.nextOffset = nextOffset;
}

@Override
public Object apply(Object source) {
/*
* Make sure all names have been assigned in the prior layers
*/
if (!initialLayer) {
if (source instanceof FastThreadLocal threadLocal) {
var name = threadLocal.getName();
VMError.guarantee(threadLocalAssignmentMap.containsKey(name), "Found thread local which was not created in the initial layer %s", name);
}
}
return super.apply(source);
}

@Override
public int sortAndAssignOffsets() {
if (initialLayer) {
assert nextOffset == -1 : nextOffset;

nextOffset = super.sortAndAssignOffsets();
} else {
assert nextOffset != -1;

for (VMThreadLocalInfo info : threadLocals.values()) {
var assignment = threadLocalAssignmentMap.get(info.name);
info.offset = assignment.offset();
assert assignment.size() == calculateSize(info) : Assertions.errorMessage("Mismatch in computed size: ", assignment.size(), calculateSize(info), info.name);
info.sizeInBytes = assignment.size();
}
}

return nextOffset;
}

@Override
public int getOffset(FastThreadLocal threadLocal) {
if (initialLayer) {
return super.getOffset(threadLocal);
} else {
return threadLocalAssignmentMap.get(threadLocal.getName()).offset();
}
}

@Override
public PersistFlags preparePersist(ImageSingletonWriter writer) {
/*
* Store the (name, offset, size) tuple of all thread locals.
*/
List<String> threadLocalNames = new ArrayList<>();
List<Integer> threadLocalOffsets = new ArrayList<>();
List<Integer> threadLocalSizes = new ArrayList<>();
if (initialLayer) {
for (var threadLocal : getSortedThreadLocalInfos()) {
threadLocalNames.add(threadLocal.name);
threadLocalOffsets.add(threadLocal.offset);
threadLocalSizes.add(threadLocal.sizeInBytes);
}
} else {
for (var entry : threadLocalAssignmentMap.entrySet()) {
threadLocalNames.add(entry.getKey());
threadLocalOffsets.add(entry.getValue().offset());
threadLocalSizes.add(entry.getValue().size());
}
}

writer.writeStringList("threadLocalNames", threadLocalNames);
writer.writeIntList("threadLocalOffsets", threadLocalOffsets);
writer.writeIntList("threadLocalSizes", threadLocalSizes);

/*
* Note while it is not strictly necessary to store nextOffset at the moment, if in the
* future we allow multiple layers to define thread locals then this information will need
* to be propagated.
*/
writer.writeInt("nextOffset", nextOffset);
return PersistFlags.CREATE;
}

@SuppressWarnings("unused")
public static Object createFromLoader(ImageSingletonLoader loader) {
/*
* Load the (name, offset, size) tuple of all thread locals.
*/
HashMap<String, ThreadInfo> threadLocalAssignmentMap = new HashMap<>();
Iterator<String> threadLocalNames = loader.readStringList("threadLocalNames").iterator();
Iterator<Integer> threadLocalOffsets = loader.readIntList("threadLocalOffsets").iterator();
Iterator<Integer> threadLocalSizes = loader.readIntList("threadLocalSizes").iterator();

while (threadLocalNames.hasNext()) {
String threadLocalName = threadLocalNames.next();
int threadLocalOffset = threadLocalOffsets.next();
int threadLocalSize = threadLocalSizes.next();

var previous = threadLocalAssignmentMap.put(threadLocalName, new ThreadInfo(threadLocalSize, threadLocalOffset));
assert previous == null : previous;
}

return new LayeredVMThreadLocalCollector(Map.copyOf(threadLocalAssignmentMap), loader.readInt("nextOffset"));
}
}
Loading