package com.oracle.svm.core.thread;
import org.graalvm.nativeimage.CurrentIsolate;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.IsolateThread;
import com.oracle.svm.core.SubstrateOptions;
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.thread.VMOperationControl.OpInProgress;
import com.oracle.svm.core.util.VMError;
public abstract class VMOperation {
private final String name;
private final SystemEffect systemEffect;
protected VMOperation(String name, SystemEffect systemEffect) {
this.name = name;
this.systemEffect = systemEffect;
}
public final String getName() {
return name;
}
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
protected boolean isGC() {
return false;
}
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public final boolean getCausesSafepoint() {
return systemEffect == SystemEffect.SAFEPOINT;
}
protected final void execute(NativeVMOperationData data) {
assert VMOperationControl.mayExecuteVmOperations();
assert !isFinished(data);
final Log trace = SubstrateOptions.TraceVMOperations.getValue() ? Log.log() : Log.noopLog();
if (!hasWork(data)) {
trace.string("[Skipping operation ").string(name).string("]");
return;
}
VMOperationControl control = ImageSingletons.lookup(VMOperationControl.class);
VMOperation prevOperation = control.getInProgress().getOperation();
IsolateThread prevQueuingThread = control.getInProgress().getQueuingThread();
IsolateThread prevExecutingThread = control.getInProgress().getExecutingThread();
control.setInProgress(this, getQueuingThread(data), CurrentIsolate.getCurrentThread());
try {
trace.string("[Executing operation ").string(name);
operate(data);
trace.string("]");
} catch (Throwable t) {
trace.string("[VMOperation.execute caught: ").string(t.getClass().getName()).string("]").newline();
throw VMError.shouldNotReachHere(t);
} finally {
control.setInProgress(prevOperation, prevQueuingThread, prevExecutingThread);
}
}
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static boolean isInProgress() {
OpInProgress inProgress = VMOperationControl.get().getInProgress();
return isInProgress(inProgress);
}
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static boolean isInProgressAtSafepoint() {
OpInProgress inProgress = VMOperationControl.get().getInProgress();
return isInProgress(inProgress) && inProgress.operation.getCausesSafepoint();
}
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
private static boolean isInProgress(OpInProgress inProgress) {
return inProgress.getExecutingThread() == CurrentIsolate.getCurrentThread();
}
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static boolean isGCInProgress() {
VMOperation op = VMOperationControl.get().getInProgress().getOperation();
return op != null && op.isGC();
}
public static void guaranteeInProgress(String message) {
if (!isInProgress()) {
throw VMError.shouldNotReachHere(message);
}
}
public static void guaranteeNotInProgress(String message) {
if (isInProgress()) {
throw VMError.shouldNotReachHere(message);
}
}
public static void guaranteeInProgressAtSafepoint(String message) {
if (!isInProgressAtSafepoint()) {
throw VMError.shouldNotReachHere(message);
}
}
public static void guaranteeGCInProgress(String message) {
if (!isGCInProgress()) {
throw VMError.shouldNotReachHere(message);
}
}
protected boolean hasWork(@SuppressWarnings("unused") NativeVMOperationData data) {
return true;
}
protected abstract IsolateThread getQueuingThread(NativeVMOperationData data);
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
protected abstract void setQueuingThread(NativeVMOperationData data, IsolateThread value);
protected abstract boolean isFinished(NativeVMOperationData data);
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
protected abstract void setFinished(NativeVMOperationData data, boolean value);
@RestrictHeapAccess(access = RestrictHeapAccess.Access.UNRESTRICTED, overridesCallers = true, reason = "Whitelisted because some operations may allocate.")
protected abstract void operate(NativeVMOperationData data);
public enum SystemEffect {
NONE,
SAFEPOINT
}
}