package com.oracle.svm.core.heap;
import static org.graalvm.compiler.nodes.extended.BranchProbabilityNode.LUDICROUSLY_FAST_PATH_PROBABILITY;
import static org.graalvm.compiler.nodes.extended.BranchProbabilityNode.probability;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import org.graalvm.compiler.core.common.SuppressFBWarnings;
import org.graalvm.compiler.debug.GraalError;
import org.graalvm.compiler.word.ObjectAccess;
import org.graalvm.compiler.word.Word;
import org.graalvm.word.Pointer;
import org.graalvm.word.WordFactory;
import com.oracle.svm.core.SubstrateUtil;
import com.oracle.svm.core.annotate.Uninterruptible;
import com.oracle.svm.core.snippets.KnownIntrinsics;
import com.oracle.svm.core.util.TimeUtils;
import com.oracle.svm.core.util.VMError;
import jdk.vm.ci.meta.MetaAccessProvider;
import jdk.vm.ci.meta.ResolvedJavaField;
import jdk.vm.ci.meta.ResolvedJavaType;
public final class ReferenceInternals {
public static final String REFERENT_FIELD_NAME = "referent";
@SuppressWarnings("unchecked")
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
static <T> Target_java_lang_ref_Reference<T> cast(Reference<T> instance) {
return SubstrateUtil.cast(instance, Target_java_lang_ref_Reference.class);
}
@SuppressWarnings("unchecked")
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
static <T> Reference<T> uncast(Target_java_lang_ref_Reference<T> instance) {
return SubstrateUtil.cast(instance, Reference.class);
}
public static <T> void clear(Reference<T> instance) {
cast(instance).referent = null;
}
public static <T> Pointer getReferentPointer(Reference<T> instance) {
return Word.objectToUntrackedPointer(ObjectAccess.readObject(instance, WordFactory.signed(Target_java_lang_ref_Reference.referentFieldOffset)));
}
@SuppressWarnings("unchecked")
public static <T> T getReferent(Reference<T> instance) {
return (T) SubstrateUtil.cast(instance, Target_java_lang_ref_Reference.class).referent;
}
public static <T> void setReferentPointer(Reference<T> instance, Pointer value) {
ObjectAccess.writeObject(instance, WordFactory.signed(Target_java_lang_ref_Reference.referentFieldOffset), value.toObject());
}
public static <T> Pointer getReferentFieldAddress(Reference<T> instance) {
return Word.objectToUntrackedPointer(instance).add(WordFactory.unsigned(Target_java_lang_ref_Reference.referentFieldOffset));
}
public static long getReferentFieldOffset() {
return Target_java_lang_ref_Reference.referentFieldOffset;
}
public static <T> Reference<?> getNextDiscovered(Reference<T> instance) {
return KnownIntrinsics.convertUnknownValue(ObjectAccess.readObject(instance, WordFactory.signed(Target_java_lang_ref_Reference.discoveredFieldOffset)), Reference.class);
}
public static long getNextDiscoveredFieldOffset() {
return Target_java_lang_ref_Reference.discoveredFieldOffset;
}
public static <T> void setNextDiscovered(Reference<T> instance, Reference<?> newNext) {
ObjectAccess.writeObject(instance, WordFactory.signed(Target_java_lang_ref_Reference.discoveredFieldOffset), newNext);
}
public static boolean hasQueue(Reference<?> instance) {
return cast(instance).queue != Target_java_lang_ref_ReferenceQueue.NULL;
}
private static final Object processPendingLock = new Object();
private static boolean processPendingActive = false;
public static void waitForPendingReferences() throws InterruptedException {
Heap.getHeap().waitForReferencePendingList();
}
@SuppressFBWarnings(value = "NN_NAKED_NOTIFY", justification = "Notifies on progress, not a specific state change.")
public static void processPendingReferences() {
Target_java_lang_ref_Reference<?> pendingList;
synchronized (processPendingLock) {
if (processPendingActive) {
return;
}
pendingList = cast(Heap.getHeap().getAndClearReferencePendingList());
if (pendingList == null) {
return;
}
processPendingActive = true;
}
try {
while (pendingList != null) {
Target_java_lang_ref_Reference<?> ref = pendingList;
pendingList = ref.discovered;
ref.discovered = null;
if (Target_jdk_internal_ref_Cleaner.class.isInstance(ref)) {
Target_jdk_internal_ref_Cleaner cleaner = Target_jdk_internal_ref_Cleaner.class.cast(ref);
cleaner.clean();
synchronized (processPendingLock) {
processPendingLock.notifyAll();
}
} else if (hasQueue(uncast(ref))) {
enqueueDirectly(ref);
}
}
} catch (Throwable t) {
VMError.shouldNotReachHere("ReferenceQueue and Cleaner must handle all potential exceptions", t);
} finally {
synchronized (processPendingLock) {
processPendingActive = false;
processPendingLock.notifyAll();
}
}
}
private static <T> void enqueueDirectly(Target_java_lang_ref_Reference<T> ref) {
ref.queue.enqueue(ref);
}
@SuppressFBWarnings(value = "WA_NOT_IN_LOOP", justification = "Wait for progress, not necessarily completion.")
public static boolean waitForReferenceProcessing() throws InterruptedException {
synchronized (processPendingLock) {
if (processPendingActive || Heap.getHeap().hasReferencePendingList()) {
processPendingLock.wait();
return true;
} else {
return false;
}
}
}
public static long getSoftReferenceClock() {
return Target_java_lang_ref_SoftReference.clock;
}
public static void updateSoftReferenceClock() {
long now = TimeUtils.divideNanosToMillis(System.nanoTime());
if (probability(LUDICROUSLY_FAST_PATH_PROBABILITY, now >= Target_java_lang_ref_SoftReference.clock)) {
Target_java_lang_ref_SoftReference.clock = now;
}
}
public static long getSoftReferenceTimestamp(SoftReference<?> instance) {
Target_java_lang_ref_SoftReference<?> ref = SubstrateUtil.cast(instance, Target_java_lang_ref_SoftReference.class);
return ref.timestamp;
}
public static ResolvedJavaType getReferenceType(MetaAccessProvider metaAccess) {
return metaAccess.lookupJavaType(Reference.class);
}
public static ResolvedJavaField getReferentField(MetaAccessProvider metaAccess) {
return getField(getReferenceType(metaAccess), ReferenceInternals.REFERENT_FIELD_NAME);
}
private static ResolvedJavaField getField(ResolvedJavaType type, String fieldName) {
for (ResolvedJavaField field : type.getInstanceFields(true)) {
if (field.getName().equals(fieldName)) {
return field;
}
}
throw new GraalError("missing field " + fieldName + " in type " + type);
}
private ReferenceInternals() {
}
}