diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GCImpl.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GCImpl.java index 97682adab097..a95c2afc9fcf 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GCImpl.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GCImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2019, 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 @@ -45,12 +45,17 @@ import org.graalvm.nativeimage.IsolateThread; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; +import org.graalvm.nativeimage.StackValue; import org.graalvm.nativeimage.c.function.CodePointer; +import org.graalvm.nativeimage.c.struct.RawField; +import org.graalvm.nativeimage.c.struct.RawStructure; +import org.graalvm.nativeimage.c.struct.SizeOf; import org.graalvm.nativeimage.hosted.Feature.FeatureAccess; import org.graalvm.word.Pointer; import org.graalvm.word.UnsignedWord; import org.graalvm.word.WordFactory; +import com.oracle.svm.core.MemoryUtil; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.annotate.AlwaysInline; import com.oracle.svm.core.annotate.NeverInline; @@ -61,6 +66,7 @@ import com.oracle.svm.core.heap.DiscoverableReference; import com.oracle.svm.core.heap.FramePointerMapWalker; import com.oracle.svm.core.heap.GC; +import com.oracle.svm.core.heap.GCCause; import com.oracle.svm.core.heap.NativeImageInfo; import com.oracle.svm.core.heap.NoAllocationVerifier; import com.oracle.svm.core.heap.ObjectReferenceWalker; @@ -75,6 +81,9 @@ import com.oracle.svm.core.stack.JavaStackWalker; import com.oracle.svm.core.stack.ThreadStackPrinter; import com.oracle.svm.core.thread.JavaThreads; +import com.oracle.svm.core.thread.JavaVMOperation; +import com.oracle.svm.core.thread.NativeVMOperation; +import com.oracle.svm.core.thread.NativeVMOperationData; import com.oracle.svm.core.thread.VMOperation; import com.oracle.svm.core.thread.VMThreads; import com.oracle.svm.core.util.TimeUtils; @@ -85,77 +94,46 @@ import sun.management.Util; //Checkstyle: resume +/** + * Most of the GC state is preallocated at image build time. + */ public class GCImpl implements GC { - - /** Options for this implementation. */ static final class Options { - @Option(help = "How much history to maintain about garbage collections.")// public static final HostedOptionKey GCHistory = new HostedOptionKey<>(1); } private static final int DECIMALS_IN_TIME_PRINTING = 7; - /* - * State. - * - * These are things I need during collection, so I allocate them during native image - * construction, and initialize them in the constructor. - */ - - /** - * A visitor for an Object reference that promotes the Object (if necessary) and updates the - * Object reference. - */ private final GreyToBlackObjRefVisitor greyToBlackObjRefVisitor; /** * A visitor for a frame that walks all the Object references in the frame. */ private final FramePointerMapWalker frameWalker; - /** - * A visitor for an Object that scans all the interior Object references. - */ private final GreyToBlackObjectVisitor greyToBlackObjectVisitor; - /** - * A policy instance for collectCompletely(String). - */ private final CollectionPolicy alwaysCompletelyInstance; - /** Accounting for this collection. */ private final Accounting accounting; - - /** The VMOperation for collections. */ - private final CollectionVMOperation collectVMOperation; + private final CollectionVMOperation collectOperation; private final OutOfMemoryError oldGenerationSizeExceeded; private final UnpinnedObjectReferenceWalkerException unpinnedObjectReferenceWalkerException; - /* - * Immutable state that references mutable state. - * - * Rather than make these static final, I make them final and initialize them in the - * constructor, so that new instances are made for each native image. - */ private final AllocationFreeList objectReferenceWalkerList; private final AllocationFreeList collectionWatcherList; private final NoAllocationVerifier noAllocationVerifier; private final GarbageCollectorManagementFactory gcManagementFactory; - /* - * Mutable state. - */ - private CollectionPolicy policy; private boolean completeCollection; private UnsignedWord sizeBefore; - /** Constructor for subclasses. */ @Platforms(Platform.HOSTED_ONLY.class) protected GCImpl(FeatureAccess access) { this.rememberedSetConstructor = new RememberedSetConstructor(); this.accounting = Accounting.factory(); - this.collectVMOperation = new CollectionVMOperation(); + this.collectOperation = new CollectionVMOperation(); this.collectionEpoch = WordFactory.zero(); this.objectReferenceWalkerList = AllocationFreeList.factory(); @@ -197,12 +175,8 @@ protected GCImpl(FeatureAccess access) { RuntimeSupport.getRuntimeSupport().addShutdownHook(this::printGCSummary); } - /* - * Collection methods. - */ - @Override - public void collect(String cause) { + public void collect(GCCause cause) { final UnsignedWord requestingEpoch = possibleCollectionPrologue(); /* Collect without allocating. */ collectWithoutAllocating(cause); @@ -211,21 +185,25 @@ public void collect(String cause) { } @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate in the implementation of garbage collection.") - void collectWithoutAllocating(String cause) { - /* Queue a VMOperation to do the collection. */ - collectVMOperation.enqueue(cause, getCollectionEpoch()); - OutOfMemoryError result = collectVMOperation.getResult(); - if (result != null) { - throw result; + void collectWithoutAllocating(GCCause cause) { + int size = SizeOf.get(CollectionVMOperationData.class); + CollectionVMOperationData data = StackValue.get(size); + MemoryUtil.fillToMemoryAtomic((Pointer) data, WordFactory.unsigned(size), (byte) 0); + data.setNativeVMOperation(collectOperation); + data.setCauseId(cause.getId()); + data.setRequestingEpoch(getCollectionEpoch()); + collectOperation.enqueue(data); + + if (data.getOutOfMemory()) { + throw oldGenerationSizeExceeded; } } /** The body of the VMOperation to do the collection. */ - @SuppressWarnings("try") - private OutOfMemoryError collectOperation(String cause, UnsignedWord requestingEpoch) { + private boolean collectOperation(GCCause cause, UnsignedWord requestingEpoch) { final Log trace = Log.noopLog().string("[GCImpl.collectOperation:").newline() .string(" epoch: ").unsigned(getCollectionEpoch()) - .string(" cause: ").string(cause) + .string(" cause: ").string(cause.getName()) .string(" requestingEpoch: ").unsigned(requestingEpoch) .newline(); VMOperation.guaranteeInProgress("Collection should be a VMOperation."); @@ -233,7 +211,7 @@ private OutOfMemoryError collectOperation(String cause, UnsignedWord requestingE /* if there has been a collection since the requesting epoch, then just return. */ if (getCollectionEpoch().aboveThan(requestingEpoch)) { trace.string(" epoch has moved on]").newline(); - return null; + return false; } /* Stop the mutator timer. */ @@ -248,23 +226,23 @@ private OutOfMemoryError collectOperation(String cause, UnsignedWord requestingE /* Flush chunks from thread-local lists to global lists. */ ThreadLocalAllocation.disableThreadLocalAllocation(); /* Report the heap before the collection. */ - printGCBefore(cause); + printGCBefore(cause.getName()); /* Scrub the lists I maintain, before the collection. */ scrubLists(); /* Run any collection watchers before the collection. */ visitWatchersBefore(); /* Collect. */ - collectImpl(cause); + collectImpl(cause.getName()); /* Check if out of memory. */ - final OutOfMemoryError result = checkIfOutOfMemory(); + boolean outOfMemory = checkIfOutOfMemory(); /* Run any collection watchers after the collection. */ visitWatchersAfter(); /* Reset for the next collection. */ HeapPolicy.bytesAllocatedSinceLastCollection.set(WordFactory.zero()); /* Print the heap after the collection. */ - printGCAfter(cause); + printGCAfter(cause.getName()); /* Note that the collection is finished. */ finishCollection(); @@ -272,7 +250,7 @@ private OutOfMemoryError collectOperation(String cause, UnsignedWord requestingE mutatorTimer.open(); trace.string("]").newline(); - return result; + return outOfMemory; } @SuppressWarnings("try") @@ -479,15 +457,11 @@ private void verbosePostCondition() { } } - private OutOfMemoryError checkIfOutOfMemory() { - OutOfMemoryError result = null; + private boolean checkIfOutOfMemory() { final UnsignedWord allowed = HeapPolicy.getMaximumHeapSize(); /* Only the old generation has objects in it because the young generation is empty. */ final UnsignedWord inUse = getAccounting().getOldGenerationAfterChunkBytes(); - if (allowed.belowThan(inUse)) { - result = oldGenerationSizeExceeded; - } - return result; + return allowed.belowThan(inUse); } @Fold @@ -496,7 +470,7 @@ static boolean runtimeAssertions() { } @Override - public void collectCompletely(final String cause) { + public void collectCompletely(final GCCause cause) { final CollectionPolicy oldPolicy = getPolicy(); try { setPolicy(alwaysCompletelyInstance); @@ -978,7 +952,7 @@ private void visitWatchersReport() { * the list I am walking, even though I am in a VMOperation. I consider that a small-enough * possibility. */ - VMOperation.enqueueBlockingNoSafepoint("GCImpl.visitWatchersReport", () -> { + JavaVMOperation.enqueueBlockingNoSafepoint("GCImpl.visitWatchersReport", () -> { for (CollectionWatcher watcher = collectionWatcherList.getFirst(); watcher != null; watcher = watcher.getNextElement()) { try { watcher.report(); @@ -1521,34 +1495,14 @@ private CollectionInProgressError() { private static final long serialVersionUID = -4473303241014559591L; } - public static final class CollectionVMOperation extends VMOperation { - - /* State. */ - private String cause; - private UnsignedWord requestingEpoch; - private OutOfMemoryError result; - - /** Constructor. */ - @Platforms(Platform.HOSTED_ONLY.class) - CollectionVMOperation() { - super("GarbageCollection", CallerEffect.BLOCKS_CALLER, SystemEffect.CAUSES_SAFEPOINT); - this.cause = "TooSoonToTell"; - this.requestingEpoch = WordFactory.zero(); - this.result = null; + private static class CollectionVMOperation extends NativeVMOperation { + protected CollectionVMOperation() { + super("Garbage collection", SystemEffect.CAUSES_SAFEPOINT); } - /** A convenience "enqueue" method that sets "cause" and "requestingEpoch" first. */ - void enqueue(String causeArg, UnsignedWord requestingEpochArg) { - cause = causeArg; - requestingEpoch = requestingEpochArg; - result = null; - enqueue(); - } - - /** What happens when this VMOperation executes. */ @Override @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while collecting") - public void operate() { + protected void operate(NativeVMOperationData data) { /* * Exceptions during collections are fatal. The heap is likely in an inconsistent state. * The GC must also be allocation free, i.e., we cannot allocate exception stack traces @@ -1559,17 +1513,36 @@ public void operate() { */ ImplicitExceptions.activateImplicitExceptionsAreFatal(); try { - result = HeapImpl.getHeapImpl().getGCImpl().collectOperation(cause, requestingEpoch); + CollectionVMOperationData d = (CollectionVMOperationData) data; + boolean outOfMemory = HeapImpl.getHeapImpl().getGCImpl().collectOperation(GCCause.fromId(d.getCauseId()), d.getRequestingEpoch()); + d.setOutOfMemory(outOfMemory); } catch (Throwable t) { throw VMError.shouldNotReachHere(t); } finally { ImplicitExceptions.deactivateImplicitExceptionsAreFatal(); } } + } - OutOfMemoryError getResult() { - return result; - } + @RawStructure + private interface CollectionVMOperationData extends NativeVMOperationData { + @RawField + int getCauseId(); + + @RawField + void setCauseId(int value); + + @RawField + UnsignedWord getRequestingEpoch(); + + @RawField + void setRequestingEpoch(UnsignedWord value); + + @RawField + boolean getOutOfMemory(); + + @RawField + void setOutOfMemory(boolean value); } /* Invoked by a shutdown hook registered in the GCImpl constructor. */ @@ -1588,7 +1561,7 @@ private void printGCSummary() { log.string(prefix).string("AlignedChunkSize: ").unsigned(HeapPolicy.getAlignedHeapChunkSize()).newline(); /* Add in any young objects allocated since the last collection. */ - VMOperation.enqueueBlockingSafepoint("PrintGCSummaryShutdownHook", ThreadLocalAllocation::disableThreadLocalAllocation); + JavaVMOperation.enqueueBlockingSafepoint("PrintGCSummaryShutdownHook", ThreadLocalAllocation::disableThreadLocalAllocation); final HeapImpl heap = HeapImpl.getHeapImpl(); final Space youngSpace = heap.getYoungGeneration().getSpace(); final UnsignedWord youngChunkBytes = youngSpace.getChunkBytes(); diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapVerifierImpl.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapVerifierImpl.java index c205120f3f58..15a601de7122 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapVerifierImpl.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapVerifierImpl.java @@ -40,6 +40,7 @@ import com.oracle.svm.core.hub.LayoutEncoding; import com.oracle.svm.core.log.Log; import com.oracle.svm.core.snippets.KnownIntrinsics; +import com.oracle.svm.core.thread.JavaVMOperation; import com.oracle.svm.core.thread.VMOperation; /** @@ -173,7 +174,7 @@ public boolean verifyObjectAt(Pointer ptr) { } /** A VMOperation that verifies the heap. */ - protected static final class VerifyVMOperation extends VMOperation { + protected static final class VerifyVMOperation extends JavaVMOperation { private final String message; private final HeapVerifierImpl verifier; @@ -181,7 +182,7 @@ protected static final class VerifyVMOperation extends VMOperation { private boolean result; VerifyVMOperation(String message, HeapVerifierImpl verifier, HeapVerifier.Occasion occasion) { - super("HeapVerification", CallerEffect.BLOCKS_CALLER, SystemEffect.CAUSES_SAFEPOINT); + super("HeapVerification", SystemEffect.CAUSES_SAFEPOINT); this.message = message; this.verifier = verifier; this.occasion = occasion; diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/MemoryWalkerImpl.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/MemoryWalkerImpl.java index 59455e67ca98..ab82f2fabfb6 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/MemoryWalkerImpl.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/MemoryWalkerImpl.java @@ -24,16 +24,16 @@ */ package com.oracle.svm.core.genscavenge; -import org.graalvm.nativeimage.hosted.Feature; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; +import org.graalvm.nativeimage.hosted.Feature; import com.oracle.svm.core.MemoryWalker; import com.oracle.svm.core.annotate.AutomaticFeature; import com.oracle.svm.core.annotate.RestrictHeapAccess; import com.oracle.svm.core.code.CodeInfoTable; -import com.oracle.svm.core.thread.VMOperation; +import com.oracle.svm.core.thread.JavaVMOperation; public class MemoryWalkerImpl extends MemoryWalker { @@ -51,13 +51,13 @@ public boolean visitMemory(MemoryWalker.Visitor memoryWalkerVisitor) { } /** A VMOperation that walks memory. */ - protected static final class MemoryWalkerVMOperation extends VMOperation { + protected static final class MemoryWalkerVMOperation extends JavaVMOperation { private MemoryWalker.Visitor memoryWalkerVisitor; private boolean result; protected MemoryWalkerVMOperation(MemoryWalker.Visitor memoryVisitor) { - super("MemoryWalkerImpl.visitMemory", CallerEffect.BLOCKS_CALLER, SystemEffect.CAUSES_SAFEPOINT); + super("MemoryWalkerImpl.visitMemory", SystemEffect.CAUSES_SAFEPOINT); this.memoryWalkerVisitor = memoryVisitor; this.result = false; } diff --git a/substratevm/src/com.oracle.svm.core.graal/src/com/oracle/svm/core/graal/meta/InstalledCodeBuilder.java b/substratevm/src/com.oracle.svm.core.graal/src/com/oracle/svm/core/graal/meta/InstalledCodeBuilder.java index a32f7bb4a46b..417514ab1fa9 100644 --- a/substratevm/src/com.oracle.svm.core.graal/src/com/oracle/svm/core/graal/meta/InstalledCodeBuilder.java +++ b/substratevm/src/com.oracle.svm.core.graal/src/com/oracle/svm/core/graal/meta/InstalledCodeBuilder.java @@ -70,7 +70,7 @@ import com.oracle.svm.core.meta.SubstrateObjectConstant; import com.oracle.svm.core.os.CommittedMemoryProvider; import com.oracle.svm.core.snippets.KnownIntrinsics; -import com.oracle.svm.core.thread.VMOperation; +import com.oracle.svm.core.thread.JavaVMOperation; import com.oracle.svm.core.util.VMError; import jdk.vm.ci.code.TargetDescription; @@ -309,7 +309,7 @@ private void installOperation() { RuntimeMethodInfoAccess.setData(runtimeMethodInfo, installedCode, tier, observerHandles); Throwable[] errorBox = {null}; - VMOperation.enqueueBlockingSafepoint("Install code", () -> { + JavaVMOperation.enqueueBlockingSafepoint("Install code", () -> { try { CodeInfoTable.getRuntimeCodeCache().addMethod(runtimeMethodInfo); /* diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/VMInspection.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/VMInspection.java index e4610401b293..79bb3cc9d227 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/VMInspection.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/VMInspection.java @@ -47,7 +47,7 @@ import com.oracle.svm.core.option.HostedOptionKey; import com.oracle.svm.core.stack.JavaStackWalker; import com.oracle.svm.core.stack.ThreadStackPrinter; -import com.oracle.svm.core.thread.VMOperation; +import com.oracle.svm.core.thread.JavaVMOperation; import com.oracle.svm.core.thread.VMThreads; import sun.misc.Signal; @@ -92,7 +92,7 @@ static void install() { @Override public void handle(Signal arg0) { - VMOperation.enqueueBlockingSafepoint("DumpAllStacks", () -> { + JavaVMOperation.enqueueBlockingSafepoint("DumpAllStacks", () -> { Log log = Log.log(); for (IsolateThread vmThread = VMThreads.firstThread(); VMThreads.isNonNullThread(vmThread); vmThread = VMThreads.nextThread(vmThread)) { if (vmThread == CurrentIsolate.getCurrentThread()) { @@ -163,7 +163,7 @@ static void install() { @Override public void handle(Signal arg0) { - VMOperation.enqueueBlockingSafepoint("DumpRuntimeCompilation", () -> { + JavaVMOperation.enqueueBlockingSafepoint("DumpRuntimeCompilation", () -> { Log log = Log.log(); SubstrateUtil.dumpRuntimeCompilation(log); log.flush(); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoTable.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoTable.java index dc2b9a4cd359..6f9cba53cd8e 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoTable.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoTable.java @@ -46,7 +46,7 @@ import com.oracle.svm.core.heap.ObjectReferenceVisitor; import com.oracle.svm.core.log.Log; import com.oracle.svm.core.option.HostedOptionKey; -import com.oracle.svm.core.thread.VMOperation; +import com.oracle.svm.core.thread.JavaVMOperation; import com.oracle.svm.core.util.Counter; import com.oracle.svm.core.util.CounterFeature; import com.oracle.svm.core.util.VMError; @@ -181,7 +181,7 @@ private static SubstrateInstalledCode getInstalledCode0(CodeInfo info) { public static void invalidateInstalledCode(SubstrateInstalledCode installedCode) { /* Captures "installedCode" for the VMOperation. */ - VMOperation.enqueueBlockingSafepoint("CodeInfoTable.invalidateInstalledCode", () -> { + JavaVMOperation.enqueueBlockingSafepoint("CodeInfoTable.invalidateInstalledCode", () -> { counters().invalidateInstalledCodeCount.inc(); if (installedCode.isValid()) { final RuntimeCodeInfo codeCache = getRuntimeCodeCache(); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeInfo.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeInfo.java index e4e8e1d23bd7..fd04c0b7f2ba 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeInfo.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeInfo.java @@ -29,6 +29,7 @@ import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.c.function.CodePointer; +import org.graalvm.nativeimage.hosted.Feature; import org.graalvm.word.UnsignedWord; import org.graalvm.word.WordFactory; @@ -41,6 +42,7 @@ import com.oracle.svm.core.log.Log; import com.oracle.svm.core.log.StringBuilderLog; import com.oracle.svm.core.option.RuntimeOptionKey; +import com.oracle.svm.core.thread.JavaVMOperation; import com.oracle.svm.core.thread.VMOperation; import com.oracle.svm.core.util.Counter; import com.oracle.svm.core.util.RingBuffer; @@ -142,7 +144,7 @@ private static int binarySearch(NonmovableArray a, int fromIndex, int } public void addMethod(CodeInfo info) { - VMOperation.enqueueBlockingSafepoint("AddMethod", () -> { + JavaVMOperation.enqueueBlockingSafepoint("AddMethod", () -> { InstalledCodeObserverSupport.activateObservers(RuntimeMethodInfoAccess.getCodeObserverHandles(info)); long num = logMethodOperation(info, INFO_ADD); addMethodOperation(info); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/deopt/Deoptimizer.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/deopt/Deoptimizer.java index dacc6c076ed1..4078e45f7d32 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/deopt/Deoptimizer.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/deopt/Deoptimizer.java @@ -65,6 +65,7 @@ import com.oracle.svm.core.config.ConfigurationValues; import com.oracle.svm.core.config.ObjectLayout; import com.oracle.svm.core.deopt.DeoptimizedFrame.VirtualFrame; +import com.oracle.svm.core.heap.GCCause; import com.oracle.svm.core.heap.Heap; import com.oracle.svm.core.heap.ReferenceAccess; import com.oracle.svm.core.hub.DynamicHub; @@ -78,6 +79,7 @@ import com.oracle.svm.core.stack.JavaStackWalker; import com.oracle.svm.core.stack.StackFrameVisitor; import com.oracle.svm.core.thread.JavaThreads; +import com.oracle.svm.core.thread.JavaVMOperation; import com.oracle.svm.core.thread.VMOperation; import com.oracle.svm.core.thread.VMThreads; import com.oracle.svm.core.util.RingBuffer; @@ -272,7 +274,7 @@ public static void deoptimizeAll() { @NeverInline("deoptimize must have a separate stack frame") public static void deoptimizeInRange(CodePointer fromIp, CodePointer toIp, boolean deoptAll) { /* Captures "fromIp", "toIp", and "deoptAll" for the VMOperation. */ - VMOperation.enqueueBlockingSafepoint("Deoptimizer.deoptimizeInRange", () -> { + JavaVMOperation.enqueueBlockingSafepoint("Deoptimizer.deoptimizeInRange", () -> { deoptimizeInRangeOperation(fromIp, toIp, deoptAll); }); } @@ -298,7 +300,7 @@ private static void deoptimizeInRangeOperation(CodePointer fromIp, CodePointer t } } if (testGCinDeoptimizer) { - Heap.getHeap().getGC().collect("from Deoptimizer.deoptimizeInRange because of testGCinDeoptimizer"); + Heap.getHeap().getGC().collect(GCCause.TestGCInDeoptimizer); } } @@ -329,7 +331,7 @@ public static void deoptimizeFrame(Pointer sourceSp, boolean ignoreNonDeoptimiza return; } IsolateThread currentThread = CurrentIsolate.getCurrentThread(); - VMOperation.enqueueBlockingSafepoint("DeoptimizeFrame", () -> Deoptimizer.deoptimizeFrameOperation(sourceSp, ignoreNonDeoptimizable, speculation, currentThread)); + JavaVMOperation.enqueueBlockingSafepoint("DeoptimizeFrame", () -> Deoptimizer.deoptimizeFrameOperation(sourceSp, ignoreNonDeoptimizable, speculation, currentThread)); } private static void deoptimizeFrameOperation(Pointer sourceSp, boolean ignoreNonDeoptimizable, SpeculationReason speculation, IsolateThread currentThread) { @@ -582,7 +584,7 @@ public DeoptimizedFrame deoptSourceFrame(CodePointer pc, boolean ignoreNonDeopti } /** A VMOperation to encapsulate deoptSourceFrame. */ - private static final class DeoptSourceFrameOperation extends VMOperation { + private static final class DeoptSourceFrameOperation extends JavaVMOperation { private final Deoptimizer receiver; private final CodePointer pc; @@ -591,7 +593,7 @@ private static final class DeoptSourceFrameOperation extends VMOperation { private IsolateThread thread; DeoptSourceFrameOperation(Deoptimizer receiver, CodePointer pc, boolean ignoreNonDeoptimizable, IsolateThread thread) { - super("DeoptSourceFrameOperation", CallerEffect.BLOCKS_CALLER, SystemEffect.CAUSES_SAFEPOINT); + super("DeoptSourceFrameOperation", SystemEffect.CAUSES_SAFEPOINT); this.receiver = receiver; this.pc = pc; this.ignoreNonDeoptimizable = ignoreNonDeoptimizable; @@ -935,7 +937,7 @@ private Object materializeObject(int virtualObjectId, FrameInfoQueryResult sourc materializedObjects[virtualObjectId] = obj; if (testGCinDeoptimizer) { - Heap.getHeap().getGC().collect("from Deoptimizer.materializeObject because of testGCinDeoptimizer"); + Heap.getHeap().getGC().collect(GCCause.TestGCInDeoptimizer); } while (curIdx < encodings.length) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaLangRefSubstitutions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaLangRefSubstitutions.java index 4f1e74373c4b..72c27b4cee62 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaLangRefSubstitutions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaLangRefSubstitutions.java @@ -198,17 +198,13 @@ public Object poll() { @Substitute public Object remove() throws InterruptedException { - if (VMOperation.isInProgress()) { - throw new IllegalStateException("Calling ReferenceQueue.remove() inside a VMOperation would block."); - } + VMOperation.guaranteeNotInProgress("Calling ReferenceQueue.remove() inside a VMOperation could block the VM thread and cause a deadlock."); return ReferenceWrapper.unwrap(feeble.remove()); } @Substitute public Object remove(long timeoutMillis) throws InterruptedException { - if (VMOperation.isInProgress()) { - throw new IllegalStateException("Calling ReferenceQueue.remove(long) inside a VMOperation would block."); - } + VMOperation.guaranteeNotInProgress("Calling ReferenceQueue.remove(long) inside a VMOperation could block the VM thread and cause a deadlock."); return ReferenceWrapper.unwrap(feeble.remove(timeoutMillis)); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/JavaVMOperation.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/JavaVMOperation.java new file mode 100644 index 000000000000..40f7d0e2b66f --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/JavaVMOperation.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2015, 2019, 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.thread; + +import org.graalvm.nativeimage.IsolateThread; + +import com.oracle.svm.core.SubstrateUtil.Thunk; +import com.oracle.svm.core.annotate.RestrictHeapAccess; + +/** + * The abstract base class of all VM operations that are allocated on the Java heap. Allocating the + * VM operation on the Java heap is preferable but not always possible. + */ +public abstract class JavaVMOperation extends VMOperation implements VMOperationControl.JavaAllocationFreeQueue.Element { + protected IsolateThread queuingThread; + private JavaVMOperation next; + private volatile boolean finished; + + protected JavaVMOperation(String name, SystemEffect systemEffect) { + super(name, systemEffect); + } + + @Override + public JavaVMOperation getNext() { + return next; + } + + @Override + public void setNext(JavaVMOperation value) { + assert next == null || value == null : "Must not change next abruptly."; + next = value; + } + + public void enqueue() { + VMOperationControl.get().enqueue(this); + } + + @Override + protected IsolateThread getQueuingThread(NativeVMOperationData data) { + return queuingThread; + } + + @Override + protected void setQueuingThread(NativeVMOperationData data, IsolateThread thread) { + queuingThread = thread; + } + + @Override + protected boolean isFinished(NativeVMOperationData data) { + return finished; + } + + @Override + protected void setFinished(NativeVMOperationData data, boolean value) { + finished = value; + } + + /** Convenience method for thunks that can be run by allocating a VMOperation. */ + public static void enqueueBlockingSafepoint(String name, Thunk thunk) { + ThunkOperation vmOperation = new ThunkOperation(name, SystemEffect.CAUSES_SAFEPOINT, thunk); + vmOperation.enqueue(); + } + + /** Convenience method for thunks that can be run by allocating a VMOperation. */ + public static void enqueueBlockingNoSafepoint(String name, Thunk thunk) { + ThunkOperation vmOperation = new ThunkOperation(name, SystemEffect.DOES_NOT_CAUSE_SAFEPOINT, thunk); + vmOperation.enqueue(); + } + + @Override + public final void operate(NativeVMOperationData data) { + operate(); + } + + @RestrictHeapAccess(access = RestrictHeapAccess.Access.UNRESTRICTED, overridesCallers = true, reason = "Whitelisted because some operations may allocate.") + protected abstract void operate(); + + /** A VMOperation that executes a thunk. */ + public static class ThunkOperation extends JavaVMOperation { + private Thunk thunk; + + ThunkOperation(String name, SystemEffect systemEffect, Thunk thunk) { + super(name, systemEffect); + this.thunk = thunk; + } + + @Override + public void operate() { + thunk.invoke(); + } + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/NativeVMOperation.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/NativeVMOperation.java new file mode 100644 index 000000000000..25b9084025ab --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/NativeVMOperation.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2019, 2019, 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.thread; + +import org.graalvm.nativeimage.IsolateThread; +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; + +/** + * A VM operation without mutable state. {@link NativeVMOperation} objects must be immutable and + * they must live in the image heap. All mutable state must be kept in native memory (see + * {@link NativeVMOperationData}). This construct is used in situations where we can't allocate any + * Java objects (e.g., when we need to do a GC). + */ +public abstract class NativeVMOperation extends VMOperation { + @Platforms(value = Platform.HOSTED_ONLY.class) + protected NativeVMOperation(String name, SystemEffect systemEffect) { + super(name, systemEffect); + } + + public void enqueue(NativeVMOperationData data) { + assert data.getNativeVMOperation() == this; + VMOperationControl.get().enqueue(data); + } + + public NativeVMOperationData getNext(NativeVMOperationData data) { + return data.getNext(); + } + + public void setNext(NativeVMOperationData data, NativeVMOperationData value) { + data.setNext(value); + } + + @Override + protected boolean isFinished(NativeVMOperationData data) { + return data.getFinished(); + } + + @Override + protected void setFinished(NativeVMOperationData data, boolean value) { + data.setFinished(value); + } + + @Override + protected IsolateThread getQueuingThread(NativeVMOperationData data) { + return data.getQueuingThread(); + } + + @Override + protected void setQueuingThread(NativeVMOperationData data, IsolateThread value) { + data.setQueuingThread(value); + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/NativeVMOperationData.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/NativeVMOperationData.java new file mode 100644 index 000000000000..625290e2c692 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/NativeVMOperationData.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2019, 2019, 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.thread; + +import org.graalvm.nativeimage.IsolateThread; +import org.graalvm.nativeimage.c.struct.RawField; +import org.graalvm.nativeimage.c.struct.RawStructure; +import org.graalvm.word.PointerBase; + +import com.oracle.svm.core.c.struct.PinnedObjectField; + +/** + * Holds the mutable state that is necessary for the execution of a {@link NativeVMOperation}. This + * is used in situations when we can't allocate Java objects (e.g., when a garbage collection is + * required). + */ +@RawStructure +public interface NativeVMOperationData extends PointerBase { + @PinnedObjectField + @RawField + void setNativeVMOperation(NativeVMOperation value); + + @PinnedObjectField + @RawField + NativeVMOperation getNativeVMOperation(); + + @RawField + NativeVMOperationData getNext(); + + @RawField + void setNext(NativeVMOperationData value); + + @RawField + IsolateThread getQueuingThread(); + + @RawField + void setQueuingThread(IsolateThread value); + + @RawField + boolean getFinished(); + + @RawField + void setFinished(boolean value); +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMOperation.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMOperation.java index 8c6769bc6e0c..4a1f12be8513 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMOperation.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMOperation.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2019, 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 @@ -30,132 +30,57 @@ import org.graalvm.word.WordFactory; import com.oracle.svm.core.SubstrateOptions; -import com.oracle.svm.core.SubstrateUtil.Thunk; import com.oracle.svm.core.annotate.RestrictHeapAccess; import com.oracle.svm.core.annotate.Uninterruptible; import com.oracle.svm.core.log.Log; -import com.oracle.svm.core.stack.StackOverflowCheck; -import com.oracle.svm.core.thread.Safepoint.SafepointException; +import com.oracle.svm.core.thread.VMOperationControl.OpInProgress; import com.oracle.svm.core.util.VMError; -/** The abstract base class of all VM operations. */ -public abstract class VMOperation extends VMOperationControl.AllocationFreeStack.Element { - - /** An identifier for the VMOperation. */ +public abstract class VMOperation { private final String name; - - /** A VMOperation either blocks the caller or it does not. */ - public enum CallerEffect { - DOES_NOT_BLOCK_CALLER, - BLOCKS_CALLER - } - - private final CallerEffect callerEffect; - - /** A VMOperation either causes a safepoint or it does not. */ - public enum SystemEffect { - DOES_NOT_CAUSE_SAFEPOINT, - CAUSES_SAFEPOINT - } - private final SystemEffect systemEffect; - /** - * The VMThread of the thread that queued this VMOperation. Useful if the operation needs to - * update thread-local state in the queuing thread. - */ - private IsolateThread queuingVMThread; - - /** - * The thread that is currently executing this VMOperation, or NULL if the operation is - * currently not being executed. - */ - private IsolateThread executingVMThread; - - /** Constructor for sub-classes. */ - protected VMOperation(String name, CallerEffect callerEffect, SystemEffect systemEffect) { - super(); + protected VMOperation(String name, SystemEffect systemEffect) { this.name = name; - this.callerEffect = callerEffect; this.systemEffect = systemEffect; - /* - * TODO: Currently I am running VMOperations on the thread of the caller, so all - * VMOperations block the caller. - */ - assert callerEffect == CallerEffect.BLOCKS_CALLER : "Only blocking calls are implemented"; - } - - /** Public interface: Queue the operation for execution. */ - public final void enqueue() { - try { - StackOverflowCheck.singleton().makeYellowZoneAvailable(); - - if (!SubstrateOptions.MultiThreaded.getValue()) { - // If I am single-threaded, I can just execute the operation. - execute(); - } else { - // If I am multi-threaded, then I have to bring the system to a safepoint, etc. - setQueuingVMThread(CurrentIsolate.getCurrentThread()); - VMOperationControl.enqueue(this); - setQueuingVMThread(WordFactory.nullPointer()); - } - } catch (SafepointException se) { - /* This exception is intended to be thrown from safepoint checks, at one's own risk */ - throw rethrow(se.inner); - - } finally { - StackOverflowCheck.singleton().protectYellowZone(); - } } - @SuppressWarnings({"unchecked"}) - static RuntimeException rethrow(Throwable ex) throws E { - throw (E) ex; + public final String getName() { + return name; } - /** Convenience method for thunks that can be run by allocating a VMOperation. */ - public static void enqueueBlockingSafepoint(String name, Thunk thunk) { - ThunkOperation vmOperation = new ThunkOperation(name, CallerEffect.BLOCKS_CALLER, SystemEffect.CAUSES_SAFEPOINT, thunk); - vmOperation.enqueue(); + public final boolean getCausesSafepoint() { + return systemEffect == SystemEffect.CAUSES_SAFEPOINT; } - /** Convenience method for thunks that can be run by allocating a VMOperation. */ - public static void enqueueBlockingNoSafepoint(String name, Thunk thunk) { - ThunkOperation vmOperation = new ThunkOperation(name, CallerEffect.BLOCKS_CALLER, SystemEffect.DOES_NOT_CAUSE_SAFEPOINT, thunk); - vmOperation.enqueue(); - } + protected final void execute(NativeVMOperationData data) { + final Log trace = SubstrateOptions.TraceVMOperations.getValue() ? Log.log() : Log.noopLog(); + VMOperationControl control = ImageSingletons.lookup(VMOperationControl.class); + VMOperation prevOperation = control.getInProgress().getOperation(); + IsolateThread prevQueuingThread = control.getInProgress().getQueuingThread(); + IsolateThread prevExecutingThread = control.getInProgress().getExecutingThread(); - /** What it means to execute an operation. */ - protected final void execute() { + control.setInProgress(this, getQueuingThread(data), CurrentIsolate.getCurrentThread()); + StackOverflowCheck.singleton().makeYellowZoneAvailable(); try { - operateUnderIndicator(); + trace.string("[Executing operation ").string(name); + operate(data); + trace.string("]"); } catch (Throwable t) { - Log.log().string("[VMOperation.execute caught: ").string(t.getClass().getName()).string("]").newline(); + trace.string("[VMOperation.execute caught: ").string(t.getClass().getName()).string("]").newline(); throw VMError.shouldNotReachHere(t); - } - } - - /* - * TODO: This method should be annotated with {@link MustNotSynchronize}, but too many methods - * would have to be white-listed to make that practical. - */ - private void operateUnderIndicator() { - final VMOperationControl control = ImageSingletons.lookup(VMOperationControl.class); - final VMOperation previousInProgress = control.getInProgress(); - try { - executingVMThread = CurrentIsolate.getCurrentThread(); - control.setInProgress(this); - operate(); } finally { - control.setInProgress(previousInProgress); - executingVMThread = WordFactory.nullPointer(); + StackOverflowCheck.singleton().protectYellowZone(); + control.setInProgress(prevOperation, prevQueuingThread, prevExecutingThread); + setQueuingThread(data, WordFactory.nullPointer()); + setFinished(data, true); } } @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public static boolean isInProgress() { - VMOperation cur = ImageSingletons.lookup(VMOperationControl.class).getInProgress(); - return cur != null && cur.executingVMThread == CurrentIsolate.getCurrentThread(); + OpInProgress inProgress = VMOperationControl.get().getInProgress(); + return inProgress.getExecutingThread() == CurrentIsolate.getCurrentThread(); } /** Check that there is a VMOperation in progress. */ @@ -172,55 +97,19 @@ public static void guaranteeNotInProgress(String message) { } } - /* - * Methods for sub-classes to override - */ - - /** Do whatever it is that this VM operation does. */ - @RestrictHeapAccess(access = RestrictHeapAccess.Access.UNRESTRICTED, overridesCallers = true, reason = "Whitelisted because some operations may allocate.") - protected abstract void operate(); - - /* - * Field access methods. - */ - - protected final String getName() { - return name; - } + protected abstract IsolateThread getQueuingThread(NativeVMOperationData data); - final boolean getBlocksCaller() { - return callerEffect == CallerEffect.BLOCKS_CALLER; - } + protected abstract void setQueuingThread(NativeVMOperationData data, IsolateThread value); - final boolean getCausesSafepoint() { - return systemEffect == SystemEffect.CAUSES_SAFEPOINT; - } + protected abstract boolean isFinished(NativeVMOperationData data); - protected final IsolateThread getQueuingVMThread() { - return queuingVMThread; - } + protected abstract void setFinished(NativeVMOperationData data, boolean value); - private void setQueuingVMThread(IsolateThread vmThread) { - queuingVMThread = vmThread; - } - - final IsolateThread getExecutingVMThread() { - return executingVMThread; - } - - /** A VMOperation that executes a thunk. */ - public static class ThunkOperation extends VMOperation { - - private Thunk thunk; - - ThunkOperation(String name, CallerEffect callerEffect, SystemEffect systemEffect, Thunk thunk) { - super(name, callerEffect, systemEffect); - this.thunk = thunk; - } + @RestrictHeapAccess(access = RestrictHeapAccess.Access.UNRESTRICTED, overridesCallers = true, reason = "Whitelisted because some operations may allocate.") + protected abstract void operate(NativeVMOperationData data); - @Override - public void operate() { - thunk.invoke(); - } + public enum SystemEffect { + DOES_NOT_CAUSE_SAFEPOINT, + CAUSES_SAFEPOINT } }