[K/N] Support of @Volatile annotation

^KT-54944
This commit is contained in:
Pavel Kunyavskiy
2022-11-14 17:08:28 +01:00
committed by Space Team
parent a29c418b63
commit 9dea349752
20 changed files with 631 additions and 295 deletions
@@ -34,6 +34,10 @@ touchFunction(AllocArrayInstance)
touchFunction(InitAndRegisterGlobal)
touchFunction(UpdateHeapRef)
touchFunction(UpdateStackRef)
touchFunction(UpdateVolatileHeapRef)
touchFunction(CompareAndSwapVolatileHeapRef)
touchFunction(CompareAndSetVolatileHeapRef)
touchFunction(GetAndSetVolatileHeapRef)
touchFunction(UpdateReturnRef)
touchFunction(ZeroHeapRef)
touchFunction(ZeroArrayRefs)
@@ -3343,6 +3343,20 @@ RUNTIME_NOTHROW void UpdateHeapRefRelaxed(ObjHeader** location, const ObjHeader*
updateHeapRef<false>(location, object);
}
ALWAYS_INLINE RUNTIME_NOTHROW void UpdateVolatileHeapRef(ObjHeader** location, const ObjHeader* object) {
RuntimeFail("Shouldn't be used");
}
extern "C" ALWAYS_INLINE RUNTIME_NOTHROW OBJ_GETTER(CompareAndSwapVolatileHeapRef, ObjHeader** location, ObjHeader* expectedValue, ObjHeader* newValue) {
RuntimeFail("Shouldn't be used");
}
extern "C" ALWAYS_INLINE RUNTIME_NOTHROW bool CompareAndSetVolatileHeapRef(ObjHeader** location, ObjHeader* expectedValue, ObjHeader* newValue) {
RuntimeFail("Shouldn't be used");
}
extern "C" ALWAYS_INLINE RUNTIME_NOTHROW OBJ_GETTER(GetAndSetVolatileHeapRef, ObjHeader** location, ObjHeader* newValue) {
RuntimeFail("Shouldn't be used");
}
RUNTIME_NOTHROW void UpdateHeapRefsInsideOneArrayStrict(const ArrayHeader* array, int fromIndex, int toIndex,
int count) {
updateHeapRefsInsideOneArray<true>(array, fromIndex, toIndex, count);
@@ -29,41 +29,6 @@ struct AtomicReferenceLayout {
KInt cookie_;
};
template<typename T> struct AtomicPrimitive {
ObjHeader header;
volatile T value_;
};
template <typename T> inline volatile T* getValueLocation(KRef thiz) {
AtomicPrimitive<T>* atomic = reinterpret_cast<AtomicPrimitive<T>*>(thiz);
return &atomic->value_;
}
template <typename T> void setImpl(KRef thiz, T value) {
volatile T* location = getValueLocation<T>(thiz);
atomicSet(location, value);
}
template <typename T> T getImpl(KRef thiz) {
volatile T* location = getValueLocation<T>(thiz);
return atomicGet(location);
}
template <typename T> T addAndGetImpl(KRef thiz, T delta) {
volatile T* location = getValueLocation<T>(thiz);
return atomicAdd(location, delta);
}
template <typename T> T compareAndSwapImpl(KRef thiz, T expectedValue, T newValue) {
volatile T* location = getValueLocation<T>(thiz);
return compareAndSwap(location, expectedValue, newValue);
}
template <typename T> KBoolean compareAndSetImpl(KRef thiz, T expectedValue, T newValue) {
volatile T* location = getValueLocation<T>(thiz);
return compareAndSet(location, expectedValue, newValue);
}
inline AtomicReferenceLayout* asAtomicReference(KRef thiz) {
return reinterpret_cast<AtomicReferenceLayout*>(thiz);
}
@@ -72,119 +37,7 @@ inline AtomicReferenceLayout* asAtomicReference(KRef thiz) {
extern "C" {
KInt Kotlin_AtomicInt_addAndGet(KRef thiz, KInt delta) {
return addAndGetImpl(thiz, delta);
}
KInt Kotlin_AtomicInt_compareAndSwap(KRef thiz, KInt expectedValue, KInt newValue) {
return compareAndSwapImpl(thiz, expectedValue, newValue);
}
KBoolean Kotlin_AtomicInt_compareAndSet(KRef thiz, KInt expectedValue, KInt newValue) {
return compareAndSetImpl(thiz, expectedValue, newValue);
}
void Kotlin_AtomicInt_set(KRef thiz, KInt newValue) {
setImpl(thiz, newValue);
}
KInt Kotlin_AtomicInt_get(KRef thiz) {
return getImpl<KInt>(thiz);
}
#if KONAN_NO_64BIT_ATOMIC
static int lock64 = 0;
#endif
KLong Kotlin_AtomicLong_addAndGet(KRef thiz, KLong delta) {
#if KONAN_NO_64BIT_ATOMIC
// Potentially huge performance penalty, but correct.
while (compareAndSwap(&lock64, 0, 1) != 0);
volatile KLong* address = getValueLocation<KLong>(thiz);
KLong old = *address;
KLong newValue = old + delta;
*address = newValue;
compareAndSwap(&lock64, 1, 0);
return newValue;
#else
return addAndGetImpl(thiz, delta);
#endif
}
KLong Kotlin_AtomicLong_compareAndSwap(KRef thiz, KLong expectedValue, KLong newValue) {
#if KONAN_NO_64BIT_ATOMIC
// Potentially huge performance penalty, but correct.
while (compareAndSwap(&lock64, 0, 1) != 0);
volatile KLong* address = getValueLocation<KLong>(thiz);
KLong old = *address;
if (old == expectedValue) {
*address = newValue;
}
compareAndSwap(&lock64, 1, 0);
return old;
#else
return compareAndSwapImpl(thiz, expectedValue, newValue);
#endif
}
KBoolean Kotlin_AtomicLong_compareAndSet(KRef thiz, KLong expectedValue, KLong newValue) {
#if KONAN_NO_64BIT_ATOMIC
// Potentially huge performance penalty, but correct.
KBoolean result = false;
while (compareAndSwap(&lock64, 0, 1) != 0);
volatile KLong* address = getValueLocation<KLong>(thiz);
KLong old = *address;
if (old == expectedValue) {
result = true;
*address = newValue;
}
compareAndSwap(&lock64, 1, 0);
return result;
#else
return compareAndSetImpl(thiz, expectedValue, newValue);
#endif
}
void Kotlin_AtomicLong_set(KRef thiz, KLong newValue) {
#if KONAN_NO_64BIT_ATOMIC
// Potentially huge performance penalty, but correct.
while (compareAndSwap(&lock64, 0, 1) != 0);
volatile KLong* address = getValueLocation<KLong>(thiz);
*address = newValue;
compareAndSwap(&lock64, 1, 0);
#else
setImpl(thiz, newValue);
#endif
}
KLong Kotlin_AtomicLong_get(KRef thiz) {
#if KONAN_NO_64BIT_ATOMIC
// Potentially huge performance penalty, but correct.
while (compareAndSwap(&lock64, 0, 1) != 0);
volatile KLong* address = getValueLocation<KLong>(thiz);
KLong value = *address;
compareAndSwap(&lock64, 1, 0);
return value;
#else
return getImpl<KLong>(thiz);
#endif
}
KNativePtr Kotlin_AtomicNativePtr_compareAndSwap(KRef thiz, KNativePtr expectedValue, KNativePtr newValue) {
return compareAndSwapImpl(thiz, expectedValue, newValue);
}
KBoolean Kotlin_AtomicNativePtr_compareAndSet(KRef thiz, KNativePtr expectedValue, KNativePtr newValue) {
return compareAndSetImpl(thiz, expectedValue, newValue);
}
void Kotlin_AtomicNativePtr_set(KRef thiz, KNativePtr newValue) {
setImpl(thiz, newValue);
}
KNativePtr Kotlin_AtomicNativePtr_get(KRef thiz) {
return getImpl<KNativePtr>(thiz);
}
void Kotlin_AtomicReference_checkIfFrozen(KRef value) {
if (!kotlin::compiler::freezingEnabled()) {
@@ -247,6 +247,12 @@ void ZeroStackRef(ObjHeader** location) RUNTIME_NOTHROW;
void UpdateStackRef(ObjHeader** location, const ObjHeader* object) RUNTIME_NOTHROW;
// Updates heap/static data location.
void UpdateHeapRef(ObjHeader** location, const ObjHeader* object) RUNTIME_NOTHROW;
// Updates volatile heap/static data location.
void UpdateVolatileHeapRef(ObjHeader** location, const ObjHeader* object) RUNTIME_NOTHROW;
OBJ_GETTER(CompareAndSwapVolatileHeapRef, ObjHeader** location, ObjHeader* expectedValue, ObjHeader* newValue) RUNTIME_NOTHROW;
bool CompareAndSetVolatileHeapRef(ObjHeader** location, ObjHeader* expectedValue, ObjHeader* newValue) RUNTIME_NOTHROW;
OBJ_GETTER(GetAndSetVolatileHeapRef, ObjHeader** location, ObjHeader* newValue) RUNTIME_NOTHROW;
// Updates heap/static data in one array.
void UpdateHeapRefsInsideOneArray(const ArrayHeader* array, int fromIndex, int toIndex, int count) RUNTIME_NOTHROW;
// Updates location if it is null, atomically.
@@ -7,6 +7,8 @@ package kotlin.native.concurrent
import kotlinx.cinterop.NativePtr
import kotlin.native.internal.*
import kotlin.reflect.*
import kotlin.concurrent.*
/**
* Wrapper around [Int] with atomic synchronized operations.
@@ -16,23 +18,15 @@ import kotlin.native.internal.*
* So shared frozen objects can have mutable fields of [AtomicInt] type.
*/
@Frozen
@OptIn(FreezingIsDeprecated::class)
public class AtomicInt(private var value_: Int) {
/**
* The value being held by this class.
*/
public var value: Int
get() = getImpl()
set(new) = setImpl(new)
@OptIn(FreezingIsDeprecated::class, ExperimentalStdlibApi::class)
public class AtomicInt(public @Volatile var value: Int) {
/**
* Increments the value by [delta] and returns the new value.
*
* @param delta the value to add
* @return the new value
*/
@GCUnsafeCall("Kotlin_AtomicInt_addAndGet")
external public fun addAndGet(delta: Int): Int
public fun addAndGet(delta: Int): Int = getAndAddField(AtomicInt::value, delta) + delta
/**
* Compares value with [expected] and replaces it with [new] value if values matches.
@@ -41,8 +35,7 @@ public class AtomicInt(private var value_: Int) {
* @param new the new value
* @return the old value
*/
@GCUnsafeCall("Kotlin_AtomicInt_compareAndSwap")
external public fun compareAndSwap(expected: Int, new: Int): Int
public fun compareAndSwap(expected: Int, new: Int): Int = compareAndSwapField(AtomicInt::value, expected, new)
/**
* Compares value with [expected] and replaces it with [new] value if values matches.
@@ -51,8 +44,7 @@ public class AtomicInt(private var value_: Int) {
* @param new the new value
* @return true if successful
*/
@GCUnsafeCall("Kotlin_AtomicInt_compareAndSet")
external public fun compareAndSet(expected: Int, new: Int): Boolean
public fun compareAndSet(expected: Int, new: Int): Boolean = compareAndSetField(AtomicInt::value, expected, new)
/**
* Increments value by one.
@@ -74,13 +66,6 @@ public class AtomicInt(private var value_: Int) {
* @return the string representation
*/
public override fun toString(): String = value.toString()
// Implementation details.
@GCUnsafeCall("Kotlin_AtomicInt_set")
private external fun setImpl(new: Int): Unit
@GCUnsafeCall("Kotlin_AtomicInt_get")
private external fun getImpl(): Int
}
/**
@@ -91,14 +76,8 @@ public class AtomicInt(private var value_: Int) {
* So shared frozen objects can have mutable fields of [AtomicLong] type.
*/
@Frozen
@OptIn(FreezingIsDeprecated::class)
public class AtomicLong(private var value_: Long = 0) {
/**
* The value being held by this class.
*/
public var value: Long
get() = getImpl()
set(new) = setImpl(new)
@OptIn(FreezingIsDeprecated::class, ExperimentalStdlibApi::class)
public class AtomicLong(public @Volatile var value: Long = 0) {
/**
* Increments the value by [delta] and returns the new value.
@@ -106,8 +85,7 @@ public class AtomicLong(private var value_: Long = 0) {
* @param delta the value to add
* @return the new value
*/
@GCUnsafeCall("Kotlin_AtomicLong_addAndGet")
external public fun addAndGet(delta: Long): Long
public fun addAndGet(delta: Long): Long = getAndAddField(AtomicLong::value, delta) + delta
/**
* Increments the value by [delta] and returns the new value.
@@ -124,8 +102,7 @@ public class AtomicLong(private var value_: Long = 0) {
* @param new the new value
* @return the old value
*/
@GCUnsafeCall("Kotlin_AtomicLong_compareAndSwap")
external public fun compareAndSwap(expected: Long, new: Long): Long
public fun compareAndSwap(expected: Long, new: Long): Long = compareAndSwapField(AtomicLong::value, expected, new)
/**
* Compares value with [expected] and replaces it with [new] value if values matches.
@@ -134,8 +111,7 @@ public class AtomicLong(private var value_: Long = 0) {
* @param new the new value
* @return true if successful, false if state is unchanged
*/
@GCUnsafeCall("Kotlin_AtomicLong_compareAndSet")
external public fun compareAndSet(expected: Long, new: Long): Boolean
public fun compareAndSet(expected: Long, new: Long): Boolean = compareAndSetField(AtomicLong::value, expected, new)
/**
* Increments value by one.
@@ -157,13 +133,6 @@ public class AtomicLong(private var value_: Long = 0) {
* @return the string representation of this object
*/
public override fun toString(): String = value.toString()
// Implementation details.
@GCUnsafeCall("Kotlin_AtomicLong_set")
private external fun setImpl(new: Long): Unit
@GCUnsafeCall("Kotlin_AtomicLong_get")
private external fun getImpl(): Long
}
/**
@@ -174,14 +143,8 @@ public class AtomicLong(private var value_: Long = 0) {
* So shared frozen objects can have mutable fields of [AtomicNativePtr] type.
*/
@Frozen
@OptIn(FreezingIsDeprecated::class)
public class AtomicNativePtr(private var value_: NativePtr) {
/**
* The value being held by this class.
*/
public var value: NativePtr
get() = getImpl()
set(new) = setImpl(new)
@OptIn(FreezingIsDeprecated::class, ExperimentalStdlibApi::class)
public class AtomicNativePtr(public @Volatile var value: NativePtr) {
/**
* Compares value with [expected] and replaces it with [new] value if values matches.
@@ -190,8 +153,8 @@ public class AtomicNativePtr(private var value_: NativePtr) {
* @param new the new value
* @return the old value
*/
@GCUnsafeCall("Kotlin_AtomicNativePtr_compareAndSwap")
external public fun compareAndSwap(expected: NativePtr, new: NativePtr): NativePtr
public fun compareAndSwap(expected: NativePtr, new: NativePtr): NativePtr =
compareAndSwapField(AtomicNativePtr::value, expected, new)
/**
* Compares value with [expected] and replaces it with [new] value if values matches.
@@ -200,8 +163,8 @@ public class AtomicNativePtr(private var value_: NativePtr) {
* @param new the new value
* @return true if successful
*/
@GCUnsafeCall("Kotlin_AtomicNativePtr_compareAndSet")
external public fun compareAndSet(expected: NativePtr, new: NativePtr): Boolean
public fun compareAndSet(expected: NativePtr, new: NativePtr): Boolean =
compareAndSetField(AtomicNativePtr::value, expected, new)
/**
* Returns the string representation of this object.
@@ -209,13 +172,6 @@ public class AtomicNativePtr(private var value_: NativePtr) {
* @return string representation of this object
*/
public override fun toString(): String = value.toString()
// Implementation details.
@GCUnsafeCall("Kotlin_AtomicNativePtr_set")
private external fun setImpl(new: NativePtr): Unit
@GCUnsafeCall("Kotlin_AtomicNativePtr_get")
private external fun getImpl(): NativePtr
}
@@ -431,3 +387,134 @@ public class FreezableAtomicReference<T>(private var value_: T) {
@GCUnsafeCall("Kotlin_AtomicReference_compareAndSet")
private external fun compareAndSetImpl(expected: Any?, new: Any?): Boolean
}
/**
* Compares the value of the field referenced by [fieldRef] from [this] object to [expectedValue], and if they are equal,
* atomically replaces it with [newValue].
*
* For now, it can be used only within the same file, where class [T] is defined.
* Check https://youtrack.jetbrains.com/issue/KT-55426 for details.
*
* Comparison is done by reference or value depending on field representation.
*
* If [fieldRef] is not a compile-time known reference to the property with [Volatile] annotation [IllegalArgumentException]
* would be thrown.
*
* If property referenced by [fieldRef] has nontrivial setter it will not be called.
*
* Returns true if the actual field value matched [expectedValue]
*
* Legacy MM: if [fieldRef] is a reference for a non-value represented field, [IllegalArgumentException] would be thrown.
*/
@PublishedApi
@TypedIntrinsic(IntrinsicType.COMPARE_AND_SET_FIELD)
internal external fun <T, S> T.compareAndSetField(filedRef: KMutableProperty1<T, S>, expectedValue: S, newValue: S): Boolean
/**
* Compares the value of the field referenced by [fieldRef] from [this] object to [expectedValue], and if they are equal,
* atomically replaces it with [newValue].
*
* For now, it can be used only within the same file, where class [T] is defined.
* Check https://youtrack.jetbrains.com/issue/KT-55426 for details.
*
* Comparison is done by reference or value depending on field representation.
*
* If [fieldRef] is not a compile-time known reference to the property with [Volatile] annotation [IllegalArgumentException]
* would be thrown.
*
* If property referenced by [fieldRef] has nontrivial setter it will not be called.
*
* Returns true if the actual field value before operation.
*
* Legacy MM: if [fieldRef] is a reference for a non-value represented field, [IllegalArgumentException] would be thrown.
*/
@PublishedApi
@TypedIntrinsic(IntrinsicType.COMPARE_AND_SWAP_FIELD)
internal external fun <T, S> T.compareAndSwapField(filedRef: KMutableProperty1<T, S>, expectedValue: S, newValue: S): S
/**
* Atomically sets value of the field referenced by [fieldRef] from [this] object to [newValue] and returns old field value.
*
* For now, it can be used only within the same file, where class [T] is defined.
* Check https://youtrack.jetbrains.com/issue/KT-55426 for details.
*
* If [fieldRef] is not a compile-time known reference to the property with [Volatile] annotation [IllegalArgumentException]
* would be thrown.
*
* If property referenced by [fieldRef] has nontrivial setter it will not be called.
*
* Legacy MM: if [fieldRef] is a reference for a non-value represented field, [IllegalArgumentException] would be thrown.
*/
@PublishedApi
@TypedIntrinsic(IntrinsicType.GET_AND_SET_FIELD)
internal external fun <T, S> T.getAndSetField(filedRef: KMutableProperty1<T, S>, newValue: S): S
/**
* Atomically increments value of the field referenced by [fieldRef] from [this] object by [delta] and returns old field value.
*
* For now, it can be used only within the same file, where class [T] is defined.
* Check https://youtrack.jetbrains.com/issue/KT-55426 for details.
*
* If [fieldRef] is not a compile-time known reference to the property with [Volatile] annotation [IllegalArgumentException]
* would be thrown.
*
* If property referenced by [fieldRef] has nontrivial setter it will not be called.
*
* Legacy MM: if [fieldRef] is a reference for a non-value represented field, [IllegalArgumentException] would be thrown.
*/
@PublishedApi
@TypedIntrinsic(IntrinsicType.GET_AND_ADD_FIELD)
internal external fun <T> T.getAndAddField(filedRef: KMutableProperty1<T, Short>, delta: Short): Short
/**
* Atomically increments value of the field referenced by [fieldRef] from [this] object by [delta] and returns old field value.
*
* For now, it can be used only within the same file, where class [T] is defined.
* Check https://youtrack.jetbrains.com/issue/KT-55426 for details.
*
* If [fieldRef] is not a compile-time known reference to the property with [Volatile] annotation [IllegalArgumentException]
* would be thrown.
*
* If property referenced by [fieldRef] has nontrivial setter it will not be called.
*
* Legacy MM: if [fieldRef] is a reference for a non-value represented field, [IllegalArgumentException] would be thrown.
*/
@PublishedApi
@TypedIntrinsic(IntrinsicType.GET_AND_ADD_FIELD)
internal external fun <T> T.getAndAddField(filedRef: KMutableProperty1<T, Int>, newValue: Int): Int
/**
* Atomically increments value of the field referenced by [fieldRef] from [this] object by [delta] and returns old field value.
*
* For now, it can be used only within the same file, where class [T] is defined.
* Check https://youtrack.jetbrains.com/issue/KT-55426 for details.
*
* If [fieldRef] is not a compile-time known reference to the property with [Volatile] annotation [IllegalArgumentException]
* would be thrown.
*
* If property referenced by [fieldRef] has nontrivial setter it will not be called.
*
* Legacy MM: if [fieldRef] is a reference for a non-value represented field, [IllegalArgumentException] would be thrown.
*/
@PublishedApi
@TypedIntrinsic(IntrinsicType.GET_AND_ADD_FIELD)
internal external fun <T> T.getAndAddField(filedRef: KMutableProperty1<T, Long>, newValue: Long): Long
/**
* Atomically increments value of the field referenced by [fieldRef] from [this] object by [delta] and returns old field value.
*
* For now, it can be used only within the same file, where class [T] is defined.
* Check https://youtrack.jetbrains.com/issue/KT-55426 for details.
*
* If [fieldRef] is not a compile-time known reference to the property with [Volatile] annotation [IllegalArgumentException]
* would be thrown.
*
* If property referenced by [fieldRef] has nontrivial setter it will not be called.
*
* Legacy MM: if [fieldRef] is a reference for a non-value represented field, [IllegalArgumentException] would be thrown.
*/
@PublishedApi
@TypedIntrinsic(IntrinsicType.GET_AND_ADD_FIELD)
internal external fun <T> T.getAndAddField(filedRef: KMutableProperty1<T, Byte>, newValue: Byte): Byte
@@ -80,5 +80,15 @@ class IntrinsicType {
// Worker
const val WORKER_EXECUTE = "WORKER_EXECUTE"
// Atomic
const val COMPARE_AND_SET_FIELD = "COMPARE_AND_SET_FIELD"
const val COMPARE_AND_SWAP_FIELD = "COMPARE_AND_SWAP_FIELD"
const val GET_AND_SET_FIELD = "GET_AND_SET_FIELD"
const val GET_AND_ADD_FIELD = "GET_AND_ADD_FIELD"
const val COMPARE_AND_SET = "COMPARE_AND_SET"
const val COMPARE_AND_SWAP = "COMPARE_AND_SWAP"
const val GET_AND_SET = "GET_AND_SET"
const val GET_AND_ADD = "GET_AND_ADD"
}
}
@@ -188,6 +188,22 @@ extern "C" ALWAYS_INLINE RUNTIME_NOTHROW void UpdateHeapRef(ObjHeader** location
mm::SetHeapRef(location, const_cast<ObjHeader*>(object));
}
extern "C" ALWAYS_INLINE RUNTIME_NOTHROW void UpdateVolatileHeapRef(ObjHeader** location, const ObjHeader* object) {
mm::SetHeapRefAtomicSeqCst(location, const_cast<ObjHeader*>(object));
}
extern "C" ALWAYS_INLINE RUNTIME_NOTHROW OBJ_GETTER(CompareAndSwapVolatileHeapRef, ObjHeader** location, ObjHeader* expectedValue, ObjHeader* newValue) {
RETURN_RESULT_OF(mm::CompareAndSwapHeapRef, location, expectedValue, newValue);
}
extern "C" ALWAYS_INLINE RUNTIME_NOTHROW bool CompareAndSetVolatileHeapRef(ObjHeader** location, ObjHeader* expectedValue, ObjHeader* newValue) {
return mm::CompareAndSetHeapRef(location, expectedValue, newValue);
}
extern "C" ALWAYS_INLINE RUNTIME_NOTHROW OBJ_GETTER(GetAndSetVolatileHeapRef, ObjHeader** location, ObjHeader* newValue) {
RETURN_RESULT_OF(mm::GetAndSetHeapRef, location, newValue);
}
extern "C" ALWAYS_INLINE RUNTIME_NOTHROW void UpdateHeapRefIfNull(ObjHeader** location, const ObjHeader* object) {
if (object == nullptr) return;
ObjHeader* result = nullptr; // No need to store this value in a rootset.
+20 -4
View File
@@ -34,6 +34,12 @@ ALWAYS_INLINE void mm::SetHeapRefAtomic(ObjHeader** location, ObjHeader* value)
__atomic_store_n(location, value, __ATOMIC_RELEASE);
}
ALWAYS_INLINE void mm::SetHeapRefAtomicSeqCst(ObjHeader** location, ObjHeader* value) noexcept {
AssertThreadState(ThreadState::kRunnable);
__atomic_store_n(location, value, __ATOMIC_SEQ_CST);
}
ALWAYS_INLINE OBJ_GETTER(mm::ReadHeapRefAtomic, ObjHeader** location) noexcept {
AssertThreadState(ThreadState::kRunnable);
// TODO: Make this work with GCs that can stop thread at any point.
@@ -45,14 +51,24 @@ ALWAYS_INLINE OBJ_GETTER(mm::CompareAndSwapHeapRef, ObjHeader** location, ObjHea
AssertThreadState(ThreadState::kRunnable);
// TODO: Make this work with GCs that can stop thread at any point.
ObjHeader* actual = expected;
// TODO: Do we need this strong memory model? Do we need to use strong CAS?
// This intrinsic modifies `actual` non-atomically.
__atomic_compare_exchange_n(location, &actual, value, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
// On success, we already have old value (== `expected`) in `actual`.
// On failure, we have the old value written into `actual`.
RETURN_OBJ(actual);
}
ALWAYS_INLINE bool mm::CompareAndSetHeapRef(ObjHeader** location, ObjHeader* expected, ObjHeader* value) noexcept {
AssertThreadState(ThreadState::kRunnable);
// TODO: Make this work with GCs that can stop thread at any point.
ObjHeader* actual = expected;
return __atomic_compare_exchange_n(location, &actual, value, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
}
ALWAYS_INLINE OBJ_GETTER(mm::GetAndSetHeapRef, ObjHeader** location, ObjHeader* value) noexcept {
AssertThreadState(ThreadState::kRunnable);;
auto *actual = __atomic_exchange_n(location, value, __ATOMIC_SEQ_CST);
RETURN_OBJ(actual);
}
#pragma clang diagnostic pop
OBJ_GETTER(mm::AllocateObject, ThreadData* threadData, const TypeInfo* typeInfo) noexcept {
@@ -24,8 +24,11 @@ class ThreadData;
void SetStackRef(ObjHeader** location, ObjHeader* value) noexcept;
void SetHeapRef(ObjHeader** location, ObjHeader* value) noexcept;
void SetHeapRefAtomic(ObjHeader** location, ObjHeader* value) noexcept;
void SetHeapRefAtomicSeqCst(ObjHeader** location, ObjHeader* value) noexcept;
OBJ_GETTER(ReadHeapRefAtomic, ObjHeader** location) noexcept;
OBJ_GETTER(CompareAndSwapHeapRef, ObjHeader** location, ObjHeader* expected, ObjHeader* value) noexcept;
bool CompareAndSetHeapRef(ObjHeader** location, ObjHeader* expected, ObjHeader* value) noexcept;
OBJ_GETTER(GetAndSetHeapRef, ObjHeader** location, ObjHeader* value) noexcept;
OBJ_GETTER(AllocateObject, ThreadData* threadData, const TypeInfo* typeInfo) noexcept;
OBJ_GETTER(AllocateArray, ThreadData* threadData, const TypeInfo* typeInfo, uint32_t elements) noexcept;