package com.oracle.truffle.js.builtins.helper;
import static com.oracle.truffle.js.runtime.builtins.JSArrayBufferView.typedArrayGetArrayType;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.js.runtime.BigInt;
import com.oracle.truffle.js.runtime.JSAgentWaiterList;
import com.oracle.truffle.js.runtime.JSAgentWaiterList.JSAgentWaiterListEntry;
import com.oracle.truffle.js.runtime.JSContext;
import com.oracle.truffle.js.runtime.JSRuntime;
import com.oracle.truffle.js.runtime.array.TypedArray;
import com.oracle.truffle.js.runtime.builtins.JSArrayBufferView;
import com.oracle.truffle.js.runtime.builtins.JSSharedArrayBuffer;
import com.oracle.truffle.js.runtime.util.Fences;
public final class SharedMemorySync {
private SharedMemorySync() {
}
@TruffleBoundary
public static int doVolatileGet(DynamicObject target, int intArrayOffset) {
Fences.acquireFence();
TypedArray array = typedArrayGetArrayType(target);
TypedArray.TypedIntArray<?> typedArray = (TypedArray.TypedIntArray<?>) array;
return typedArray.getInt(target, intArrayOffset);
}
@TruffleBoundary
public static BigInt doVolatileGetBigInt(DynamicObject target, int intArrayOffset) {
Fences.acquireFence();
TypedArray array = typedArrayGetArrayType(target);
TypedArray.TypedBigIntArray<?> typedArray = (TypedArray.TypedBigIntArray<?>) array;
return typedArray.getBigInt(target, intArrayOffset);
}
@TruffleBoundary
public static void doVolatilePut(DynamicObject target, int index, int value) {
TypedArray array = typedArrayGetArrayType(target);
TypedArray.TypedIntArray<?> typedArray = (TypedArray.TypedIntArray<?>) array;
typedArray.setInt(target, index, value);
Fences.releaseFence();
}
@TruffleBoundary
public static void doVolatilePutBigInt(DynamicObject target, int index, BigInt value) {
TypedArray array = typedArrayGetArrayType(target);
TypedArray.TypedBigIntArray<?> typedArray = (TypedArray.TypedBigIntArray<?>) array;
typedArray.setBigInt(target, index, value);
Fences.releaseFence();
}
@TruffleBoundary
public static boolean compareAndSwapInt(JSContext cx, DynamicObject target, int intArrayOffset, int initial, int result) {
cx.getJSAgent().atomicSectionEnter(target);
try {
int value = doVolatileGet(target, intArrayOffset);
if (value == initial) {
doVolatilePut(target, intArrayOffset, result);
return true;
}
return false;
} finally {
cx.getJSAgent().atomicSectionLeave(target);
}
}
@TruffleBoundary
public static boolean compareAndSwapBigInt(JSContext cx, DynamicObject target, int intArrayOffset, BigInt initial, BigInt result) {
cx.getJSAgent().atomicSectionEnter(target);
try {
BigInt value = doVolatileGetBigInt(target, intArrayOffset);
if (value.compareTo(initial) == 0) {
doVolatilePutBigInt(target, intArrayOffset, result);
return true;
}
return false;
} finally {
cx.getJSAgent().atomicSectionLeave(target);
}
}
@TruffleBoundary
public static long atomicFetchOrGetUnsigned(JSContext cx, DynamicObject target, int intArrayOffset, Object expected, Object replacement) {
cx.getJSAgent().atomicSectionEnter(target);
long read = JSRuntime.toUInt32(doVolatileGet(target, intArrayOffset));
if (read == JSRuntime.toUInt32(expected)) {
doVolatilePut(target, intArrayOffset, (int) JSRuntime.toUInt32(replacement));
}
cx.getJSAgent().atomicSectionLeave(target);
return read;
}
@TruffleBoundary
public static long atomicFetchOrGetLong(JSContext cx, DynamicObject target, int intArrayOffset, long expected, long replacement) {
cx.getJSAgent().atomicSectionEnter(target);
try {
int read = doVolatileGet(target, intArrayOffset);
if (read == expected) {
doVolatilePut(target, intArrayOffset, (int) replacement);
}
return read;
} finally {
cx.getJSAgent().atomicSectionLeave(target);
}
}
@TruffleBoundary
public static int atomicFetchOrGetInt(JSContext cx, DynamicObject target, int intArrayOffset, int expected, int replacement) {
cx.getJSAgent().atomicSectionEnter(target);
try {
int read = doVolatileGet(target, intArrayOffset);
if (read == expected) {
doVolatilePut(target, intArrayOffset, replacement);
}
return read;
} finally {
cx.getJSAgent().atomicSectionLeave(target);
}
}
@TruffleBoundary
public static int atomicFetchOrGetShort(JSContext cx, DynamicObject target, int intArrayOffset, int expected, int replacement, boolean sign) {
cx.getJSAgent().atomicSectionEnter(target);
int read = doVolatileGet(target, intArrayOffset);
read = sign ? read : read & 0xFFFF;
int expectedChopped = sign ? (short) expected : expected & 0xFFFF;
if (read == expectedChopped) {
int signed = sign ? replacement : replacement & 0xFFFF;
SharedMemorySync.doVolatilePut(target, intArrayOffset, (short) signed);
}
cx.getJSAgent().atomicSectionLeave(target);
return read;
}
@TruffleBoundary
public static int atomicFetchOrGetByte(JSContext cx, DynamicObject target, int intArrayOffset, int expected, int replacement, boolean sign) {
cx.getJSAgent().atomicSectionEnter(target);
try {
int read = doVolatileGet(target, intArrayOffset);
read = sign ? read : read & 0xFF;
int expectedChopped = sign ? (byte) expected : expected & 0xFF;
if (read == expectedChopped) {
int signed = sign ? replacement : replacement & 0xFF;
SharedMemorySync.doVolatilePut(target, intArrayOffset, (byte) signed);
}
return read;
} finally {
cx.getJSAgent().atomicSectionLeave(target);
}
}
@TruffleBoundary
public static BigInt atomicFetchOrGetBigInt(JSContext cx, DynamicObject target, int intArrayOffset, BigInt expected, BigInt replacement) {
cx.getJSAgent().atomicSectionEnter(target);
try {
BigInt read = doVolatileGetBigInt(target, intArrayOffset);
if (read.compareTo(expected) == 0) {
doVolatilePutBigInt(target, intArrayOffset, replacement);
}
return read;
} finally {
cx.getJSAgent().atomicSectionLeave(target);
}
}
@SuppressWarnings("unused")
public static JSAgentWaiterListEntry getWaiterList(JSContext cx, DynamicObject target, int indexPos) {
DynamicObject arrayBuffer = JSArrayBufferView.getArrayBuffer(target);
JSAgentWaiterList waiterList = JSSharedArrayBuffer.getWaiterList(arrayBuffer);
return waiterList.getListForIndex(indexPos);
}
@TruffleBoundary
public static void enterCriticalSection(JSContext cx, JSAgentWaiterListEntry wl) {
assert !cx.getJSAgent().inCriticalSection();
cx.getJSAgent().criticalSectionEnter(wl);
}
@TruffleBoundary
public static void leaveCriticalSection(JSContext cx, JSAgentWaiterListEntry wl) {
cx.getJSAgent().criticalSectionLeave(wl);
}
public static boolean agentCanSuspend(JSContext cx) {
return cx.getJSAgent().canBlock();
}
@TruffleBoundary
public static void addWaiter(JSContext cx, JSAgentWaiterListEntry wl, int id) {
assert cx.getJSAgent().inCriticalSection();
assert !wl.contains(id);
wl.add(id);
}
@TruffleBoundary
public static void removeWaiter(JSContext cx, JSAgentWaiterListEntry wl, int w) {
assert cx.getJSAgent().inCriticalSection();
assert wl.contains(w);
wl.remove(w);
}
@TruffleBoundary
public static boolean suspendAgent(JSContext cx, JSAgentWaiterListEntry wl, int w, int timeout) {
assert cx.getJSAgent().inCriticalSection();
assert wl.contains(w);
assert cx.getJSAgent().getSignifier() == w;
assert cx.getJSAgent().canBlock();
cx.getJSAgent().criticalSectionLeave(wl);
boolean interrupt = false;
try {
Thread.sleep(timeout);
} catch (InterruptedException e) {
interrupt = true;
}
cx.getJSAgent().criticalSectionEnter(wl);
return interrupt;
}
@TruffleBoundary
public static void wakeWaiter(JSContext cx, int w) {
assert cx.getJSAgent().inCriticalSection();
cx.getJSAgent().wakeAgent(w);
}
@TruffleBoundary
public static int[] removeWaiters(JSContext cx, JSAgentWaiterListEntry wl, int count) {
assert cx.getJSAgent().inCriticalSection();
int c = Integer.min(wl.size(), count);
int[] removed = new int[c];
while (c-- > 0) {
removed[c] = wl.poll();
}
return removed;
}
}