[K/N] Support of @Volatile annotation
^KT-54944
This commit is contained in:
committed by
Space Team
parent
a29c418b63
commit
9dea349752
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user