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
1 change: 1 addition & 0 deletions substratevm/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ This changelog summarizes major changes to GraalVM Native Image.
* (GR-39497) Add `-H:BuildOutputJSONFile=<file.json>` option for [JSON build output](https://github.com/oracle/graal/edit/master/docs/reference-manual/native-image/BuildOutput.md#machine-readable-build-output). Please feel free to provide feedback so that we can stabilize the schema/API.
* (GR-40170) Add `--silent` option to silence the build output.
* (GR-39475) Add initial support for jvmstat.
* (GR-39563) Add support for JDK 19 and Project Loom Virtual Threads (JEP 425) for high-throughput lightweight concurrency. Enable on JDK 19 with `native-image -J--enable-preview`.

## Version 22.2.0
* (GR-20653) Re-enable the usage of all CPU features for JIT compilation on AMD64.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
import com.oracle.svm.core.option.RuntimeOptionKey;
import com.oracle.svm.core.os.CommittedMemoryProvider;
import com.oracle.svm.core.snippets.KnownIntrinsics;
import com.oracle.svm.core.thread.JavaThreads;
import com.oracle.svm.core.thread.PlatformThreads;
import com.oracle.svm.core.thread.ThreadStatus;
import com.oracle.svm.core.thread.VMOperation;
import com.oracle.svm.core.thread.VMThreads;
Expand Down Expand Up @@ -561,12 +561,12 @@ public void waitForReferencePendingList() throws InterruptedException {

private static boolean waitForPendingReferenceList(long initialOffers, long initialWakeUps) {
Thread currentThread = Thread.currentThread();
int oldThreadStatus = JavaThreads.getThreadStatus(currentThread);
JavaThreads.setThreadStatus(currentThread, ThreadStatus.PARKED);
int oldThreadStatus = PlatformThreads.getThreadStatus(currentThread);
PlatformThreads.setThreadStatus(currentThread, ThreadStatus.PARKED);
try {
return transitionToNativeThenAwaitPendingRefs(initialOffers, initialWakeUps);
} finally {
JavaThreads.setThreadStatus(currentThread, oldThreadStatus);
PlatformThreads.setThreadStatus(currentThread, oldThreadStatus);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,16 +134,6 @@ protected void onValueUpdate(EconomicMap<OptionKey<?>, Object> values, Boolean o
@Option(help = "Support continuations (without requiring a Project Loom JDK)") //
public static final HostedOptionKey<Boolean> SupportContinuations = new HostedOptionKey<>(false);

@Option(help = "Build with Project Loom JDK") //
public static final HostedOptionKey<Boolean> UseLoom = new HostedOptionKey<>(false) {
@Override
protected void onValueUpdate(EconomicMap<OptionKey<?>, Object> values, Boolean oldValue, Boolean newValue) {
if (newValue) {
SupportContinuations.update(values, true);
}
}
};

public static final int ForceFallback = 10;
public static final int Automatic = 5;
public static final int NoFallback = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ private static int allocateFromStack(Continuation cont, Pointer baseSp, Pointer
if (!yield) {
PreemptVisitor visitor = new PreemptVisitor(baseSp);
JavaStackWalker.walkThread(targetThread, visitor);
if (visitor.preemptStatus != Continuation.YIELD_SUCCESS) {
if (visitor.preemptStatus != Continuation.FREEZE_OK) {
return visitor.preemptStatus;
}
startSp = visitor.leafSP;
Expand All @@ -138,7 +138,7 @@ private static int allocateFromStack(Continuation cont, Pointer baseSp, Pointer
StoredContinuation instance = allocate(framesSize);
fillUninterruptibly(instance, startIp, startSp, framesSize);
cont.stored = instance;
return Continuation.YIELD_SUCCESS;
return Continuation.FREEZE_OK;
}

@Uninterruptible(reason = "Prevent modifications to the stack while initializing instance and copying frames.")
Expand Down Expand Up @@ -187,39 +187,18 @@ public static boolean walkReferences(Pointer baseAddress, ObjectReferenceVisitor
StoredContinuation s = (StoredContinuation) holderObject;
assert baseAddress.equal(Word.objectToUntrackedPointer(holderObject));

CodePointer startIp = getIP(s);
if (startIp.isNull()) {
JavaStackWalk walk = StackValue.get(JavaStackWalk.class);
if (!initWalk(s, walk)) {
return true; // uninitialized, ignore
}

Pointer startSp = getFramesStart(s);
Pointer endSp = arrayAddress(s).add(getSizeInBytes(s));

JavaStackWalk walk = StackValue.get(JavaStackWalk.class);
JavaStackWalker.initWalk(walk, startSp, endSp, startIp);

SimpleCodeInfoQueryResult queryResult = StackValue.get(SimpleCodeInfoQueryResult.class);
do {
Pointer sp = walk.getSP();
CodePointer ip = walk.getPossiblyStaleIP();

UntetheredCodeInfo untetheredCodeInfo = walk.getIPCodeInfo();
Object tether = CodeInfoAccess.acquireTether(untetheredCodeInfo);
try {
CodeInfo codeInfo = CodeInfoAccess.convert(untetheredCodeInfo);
VMError.guarantee(codeInfo.equal(CodeInfoTable.getImageCodeInfo()));
VMError.guarantee(Deoptimizer.checkDeoptimized(sp) == null);
if (codeInfo.isNull()) {
throw JavaStackWalker.reportUnknownFrameEncountered(sp, ip, null);
}

CodeInfoAccess.lookupCodeInfo(codeInfo, CodeInfoAccess.relativeIP(codeInfo, ip), queryResult);

NonmovableArray<Byte> referenceMapEncoding = CodeInfoAccess.getStackReferenceMapEncoding(codeInfo);
long referenceMapIndex = queryResult.getReferenceMapIndex();
if (referenceMapIndex != ReferenceMapIndex.NO_REFERENCE_MAP) {
CodeReferenceMapDecoder.walkOffsetsFromPointer(sp, referenceMapEncoding, referenceMapIndex, visitor, holderObject);
}
CodeInfo codeInfo = CodeInfoAccess.convert(untetheredCodeInfo, tether);
walkFrameReferences(walk, codeInfo, queryResult, visitor, holderObject);
} finally {
CodeInfoAccess.releaseTether(untetheredCodeInfo, tether);
}
Expand All @@ -228,13 +207,52 @@ public static boolean walkReferences(Pointer baseAddress, ObjectReferenceVisitor
return true;
}

@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static boolean initWalk(StoredContinuation s, JavaStackWalk walk) {
CodePointer startIp = getIP(s);
if (startIp.isNull()) {
return false; // uninitialized
}

initWalk(s, walk, startIp);
return true;
}

@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static void initWalk(StoredContinuation s, JavaStackWalk walk, CodePointer startIp) {
Pointer startSp = getFramesStart(s);
Pointer endSp = arrayAddress(s).add(getSizeInBytes(s));

JavaStackWalker.initWalk(walk, startSp, endSp, startIp);
walk.setAnchor(WordFactory.nullPointer()); // never use an anchor of this platform thread
}

@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static void walkFrameReferences(JavaStackWalk walk, CodeInfo codeInfo, SimpleCodeInfoQueryResult queryResult, ObjectReferenceVisitor visitor, Object holderObject) {
Pointer sp = walk.getSP();
CodePointer ip = walk.getPossiblyStaleIP();
if (codeInfo.isNull()) {
throw JavaStackWalker.reportUnknownFrameEncountered(sp, ip, null);
}
VMError.guarantee(codeInfo.equal(CodeInfoTable.getImageCodeInfo()));
VMError.guarantee(Deoptimizer.checkDeoptimized(sp) == null);

CodeInfoAccess.lookupCodeInfo(codeInfo, CodeInfoAccess.relativeIP(codeInfo, ip), queryResult);

NonmovableArray<Byte> referenceMapEncoding = CodeInfoAccess.getStackReferenceMapEncoding(codeInfo);
long referenceMapIndex = queryResult.getReferenceMapIndex();
if (referenceMapIndex != ReferenceMapIndex.NO_REFERENCE_MAP) {
CodeReferenceMapDecoder.walkOffsetsFromPointer(sp, referenceMapEncoding, referenceMapIndex, visitor, holderObject);
}
}

private static final class PreemptVisitor extends StackFrameVisitor {
private final Pointer endSP;
private boolean startFromNextFrame = false;

Pointer leafSP;
CodePointer leafIP;
int preemptStatus = Continuation.YIELD_SUCCESS;
int preemptStatus = Continuation.FREEZE_OK;

PreemptVisitor(Pointer endSP) {
this.endSP = endSP;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,15 @@

import java.io.FileDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.Buffer;
import java.nio.MappedByteBuffer;
import java.util.Random;
import java.util.SplittableRandom;
import java.util.concurrent.ThreadLocalRandom;

import com.oracle.svm.core.thread.LoomSupport;
import com.oracle.svm.core.thread.Target_java_lang_Continuation;
import com.oracle.svm.core.SubstrateUtil;
import com.oracle.svm.core.thread.VirtualThreads;
import com.oracle.svm.core.util.VMError;
import com.oracle.svm.util.ReflectionUtil;
Expand All @@ -48,12 +49,14 @@ public interface DisallowedObjectReporter {
}

private static final Class<?> CANCELLABLE_CLASS;
private static final Class<?> JDK_VIRTUAL_THREAD_CLASS;
private static final Class<?> CONTINUATION_CLASS;
private static final Method CONTINUATION_IS_STARTED_METHOD;
static {
try {
CANCELLABLE_CLASS = Class.forName("sun.nio.fs.Cancellable");
} catch (ClassNotFoundException ex) {
throw VMError.shouldNotReachHere(ex);
}
CANCELLABLE_CLASS = ReflectionUtil.lookupClass(false, "sun.nio.fs.Cancellable");
JDK_VIRTUAL_THREAD_CLASS = ReflectionUtil.lookupClass(true, "java.lang.VirtualThread");
CONTINUATION_CLASS = ReflectionUtil.lookupClass(true, "jdk.internal.vm.Continuation");
CONTINUATION_IS_STARTED_METHOD = (CONTINUATION_CLASS == null) ? null : ReflectionUtil.lookupMethod(false, CONTINUATION_CLASS, "isStarted");
}

public static void check(Object obj, DisallowedObjectReporter reporter) {
Expand All @@ -64,24 +67,29 @@ public static void check(Object obj, DisallowedObjectReporter reporter) {
obj, "Try avoiding to initialize the class that caused initialization of the object.");
}

/* Started Threads can not be in the image heap. */
/* Started platform threads can not be in the image heap. */
if (obj instanceof Thread) {
final Thread asThread = (Thread) obj;
if (VirtualThreads.isSupported() && VirtualThreads.singleton().isVirtual(asThread)) {
if (VirtualThreads.isSupported() && (VirtualThreads.singleton().isVirtual(asThread) || (JDK_VIRTUAL_THREAD_CLASS != null && JDK_VIRTUAL_THREAD_CLASS.isInstance(asThread)))) {
// allowed unless the thread is mounted, in which case it references its carrier
// thread and fails
} else if (asThread.getState() != Thread.State.NEW && asThread.getState() != Thread.State.TERMINATED) {
throw reporter.raise("Detected a started Thread in the image heap. " +
"Threads running in the image generator are no longer running at image runtime.",
asThread, "Try avoiding to initialize the class that caused initialization of the Thread.");
asThread, "Prevent threads from starting during image generation, or a started thread from being included in the image.");
}
}
if (obj instanceof Target_java_lang_Continuation) {
final Target_java_lang_Continuation asCont = (Target_java_lang_Continuation) obj;
if (LoomSupport.isStarted(asCont)) {
if (SubstrateUtil.HOSTED && CONTINUATION_CLASS != null && CONTINUATION_CLASS.isInstance(obj)) {
boolean isStarted;
try {
isStarted = (Boolean) CONTINUATION_IS_STARTED_METHOD.invoke(obj);
} catch (IllegalAccessException | InvocationTargetException ignored) {
isStarted = false;
}
if (isStarted) {
throw reporter.raise("Detected a started Continuation in the image heap. " +
"Continuations running in the image generator are no longer running at image runtime.",
asCont, "Try avoiding to initialize the class that caused initialization of the Continuation.");
"Continuation state from the image generator cannot be used at image runtime.",
obj, "Prevent continuations from starting during image generation, or started continuations from being included in the image.");
}
}
/* FileDescriptors can not be in the image heap. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@
import com.oracle.svm.core.hub.DynamicHub;
import com.oracle.svm.core.jdk.JavaLangSubstitutions.ClassValueSupport;
import com.oracle.svm.core.monitor.MonitorSupport;
import com.oracle.svm.core.snippets.KnownIntrinsics;
import com.oracle.svm.core.snippets.SubstrateForeignCallTarget;
import com.oracle.svm.core.thread.JavaThreads;
import com.oracle.svm.core.util.VMError;

import jdk.vm.ci.meta.MetaAccessProvider;
Expand Down Expand Up @@ -99,16 +99,21 @@ private int hashCodeSubst() {
}

@Substitute
@TargetElement(name = "wait", onlyWith = JDK17OrEarlier.class)
@TargetElement(name = "wait")
private void waitSubst(long timeoutMillis) throws InterruptedException {
/*
* JDK 19 and later: our monitor implementation does not pin virtual threads, so avoid
* jdk.internal.misc.Blocker which expects and asserts that a virtual thread is pinned.
* Also, we get interrupted on the virtual thread instead of the carrier thread, which
* clears the carrier thread's interrupt status too, so we don't have to intercept an
* InterruptedException from the carrier thread to clear the virtual thread interrupt.
*/
MonitorSupport.singleton().wait(this, timeoutMillis);
}

@Substitute
@TargetElement(name = "wait0", onlyWith = JDK19OrLater.class)
private void waitSubstLoom(long timeoutMillis) throws InterruptedException {
MonitorSupport.singleton().wait(this, timeoutMillis);
}
@Delete
@TargetElement(onlyWith = JDK19OrLater.class)
private native void wait0(long timeoutMillis);

@Substitute
@TargetElement(name = "notify")
Expand Down Expand Up @@ -211,7 +216,7 @@ final class Target_java_lang_Throwable {
@Substitute
@NeverInline("Starting a stack walk in the caller frame")
private Object fillInStackTrace() {
stackTrace = StackTraceUtils.getStackTrace(true, KnownIntrinsics.readCallerStackPointer());
stackTrace = JavaThreads.getStackTrace(true, Thread.currentThread());
return this;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import org.graalvm.nativeimage.hosted.Feature;
import org.graalvm.nativeimage.impl.RuntimeClassInitializationSupport;

import com.oracle.svm.core.StaticFieldsSupport;
import com.oracle.svm.core.SubstrateUtil;
import com.oracle.svm.core.annotate.Alias;
import com.oracle.svm.core.annotate.AutomaticFeature;
Expand Down Expand Up @@ -332,6 +333,19 @@ public static int getCommonPoolParallelism() {
@TargetElement(onlyWith = JDK17OrEarlier.class)//
static int COMMON_PARALLELISM;

@Alias @TargetElement(onlyWith = JDK19OrLater.class) //
private static Unsafe U;

@Alias @TargetElement(onlyWith = JDK19OrLater.class) //
private static long POOLIDS;

@Substitute
@TargetElement(onlyWith = JDK19OrLater.class) //
private static int getAndAddPoolIds(int x) {
// Original method wrongly uses ForkJoinPool.class instead of calling U.staticFieldBase()
return U.getAndAddInt(StaticFieldsSupport.getStaticPrimitiveFields(), POOLIDS, x);
}

@Alias //
Target_java_util_concurrent_ForkJoinPool(byte forCommonPoolOnly) {
}
Expand Down
Loading