Make FreezableAtomicReference atomic when unfrozen for the new MM.

This commit is contained in:
Alexander Shabalin
2021-06-25 21:18:37 +00:00
committed by Space
parent 518e8691b7
commit 28b6427d3e
7 changed files with 28 additions and 34 deletions
@@ -596,17 +596,10 @@ func testShared() throws {
try assertFalse(ValuesKt.isFrozen(obj: obj), "isFrozen(\(obj))") try assertFalse(ValuesKt.isFrozen(obj: obj), "isFrozen(\(obj))")
} }
if ValuesKt.isExperimentalMM { try assertFrozen(NSObject())
try assertNotFrozen(NSObject()) try assertFrozen(TestSharedIImpl())
try assertNotFrozen(TestSharedIImpl()) try assertFrozen(ValuesKt.kotlinLambda(block: { return $0 }) as AnyObject)
try assertNotFrozen(ValuesKt.kotlinLambda(block: { return $0 }) as AnyObject) try assertNotFrozen(FinalClassExtOpen())
try assertNotFrozen(FinalClassExtOpen())
} else {
try assertFrozen(NSObject())
try assertFrozen(TestSharedIImpl())
try assertFrozen(ValuesKt.kotlinLambda(block: { return $0 }) as AnyObject)
try assertNotFrozen(FinalClassExtOpen())
}
} }
class PureSwiftClass { class PureSwiftClass {
@@ -59,36 +59,27 @@ data class Data(var int: Int)
assertFailsWith<InvalidMutabilityException> { a8[1] = 2.0 } assertFailsWith<InvalidMutabilityException> { a8[1] = 2.0 }
// Ensure that String and integral boxes are frozen by default, by passing local to the worker. // Ensure that String and integral boxes are frozen by default, by passing local to the worker.
val hasToBeFrozen = Platform.memoryModel == MemoryModel.STRICT
val worker = Worker.start() val worker = Worker.start()
var data: Any = "Hello" + " " + "world" var data: Any = "Hello" + " " + "world"
if (hasToBeFrozen) { assertTrue(data.isFrozen)
assertTrue(data.isFrozen)
}
worker.execute(TransferMode.SAFE, { data } ) { worker.execute(TransferMode.SAFE, { data } ) {
input -> println("Worker 1: $input") input -> println("Worker 1: $input")
}.result }.result
data = 42 data = 42
if (hasToBeFrozen) { assertTrue(data.isFrozen)
assertTrue(data.isFrozen)
}
worker.execute(TransferMode.SAFE, { data } ) { worker.execute(TransferMode.SAFE, { data } ) {
input -> println("Worker2: $input") input -> println("Worker2: $input")
}.result }.result
data = 239.0 data = 239.0
if (hasToBeFrozen) { assertTrue(data.isFrozen)
assertTrue(data.isFrozen)
}
worker.execute(TransferMode.SAFE, { data } ) { worker.execute(TransferMode.SAFE, { data } ) {
input -> println("Worker3: $input") input -> println("Worker3: $input")
}.result }.result
data = 'a' data = 'a'
if (hasToBeFrozen) { assertTrue(data.isFrozen)
assertTrue(data.isFrozen)
}
worker.execute(TransferMode.SAFE, { data } ) { worker.execute(TransferMode.SAFE, { data } ) {
input -> println("Worker4: $input") input -> println("Worker4: $input")
}.result }.result
@@ -182,7 +182,9 @@ void Kotlin_AtomicReference_checkIfFrozen(KRef value) {
} }
OBJ_GETTER(Kotlin_AtomicReference_compareAndSwap, KRef thiz, KRef expectedValue, KRef newValue) { OBJ_GETTER(Kotlin_AtomicReference_compareAndSwap, KRef thiz, KRef expectedValue, KRef newValue) {
Kotlin_AtomicReference_checkIfFrozen(newValue); if (isPermanentOrFrozen(thiz)) {
Kotlin_AtomicReference_checkIfFrozen(newValue);
}
// See Kotlin_AtomicReference_get() for explanations, why locking is needed. // See Kotlin_AtomicReference_get() for explanations, why locking is needed.
AtomicReferenceLayout* ref = asAtomicReference(thiz); AtomicReferenceLayout* ref = asAtomicReference(thiz);
RETURN_RESULT_OF(SwapHeapRefLocked, &ref->value_, expectedValue, newValue, RETURN_RESULT_OF(SwapHeapRefLocked, &ref->value_, expectedValue, newValue,
@@ -190,7 +192,9 @@ OBJ_GETTER(Kotlin_AtomicReference_compareAndSwap, KRef thiz, KRef expectedValue,
} }
KBoolean Kotlin_AtomicReference_compareAndSet(KRef thiz, KRef expectedValue, KRef newValue) { KBoolean Kotlin_AtomicReference_compareAndSet(KRef thiz, KRef expectedValue, KRef newValue) {
Kotlin_AtomicReference_checkIfFrozen(newValue); if (isPermanentOrFrozen(thiz)) {
Kotlin_AtomicReference_checkIfFrozen(newValue);
}
// See Kotlin_AtomicReference_get() for explanations, why locking is needed. // See Kotlin_AtomicReference_get() for explanations, why locking is needed.
AtomicReferenceLayout* ref = asAtomicReference(thiz); AtomicReferenceLayout* ref = asAtomicReference(thiz);
ObjHolder holder; ObjHolder holder;
@@ -200,7 +204,9 @@ KBoolean Kotlin_AtomicReference_compareAndSet(KRef thiz, KRef expectedValue, KRe
} }
void Kotlin_AtomicReference_set(KRef thiz, KRef newValue) { void Kotlin_AtomicReference_set(KRef thiz, KRef newValue) {
Kotlin_AtomicReference_checkIfFrozen(newValue); if (isPermanentOrFrozen(thiz)) {
Kotlin_AtomicReference_checkIfFrozen(newValue);
}
AtomicReferenceLayout* ref = asAtomicReference(thiz); AtomicReferenceLayout* ref = asAtomicReference(thiz);
SetHeapRefLocked(&ref->value_, newValue, &ref->lock_, &ref->cookie_); SetHeapRefLocked(&ref->value_, newValue, &ref->lock_, &ref->cookie_);
} }
@@ -326,7 +326,7 @@ public class FreezableAtomicReference<T>(private var value_: T) {
public var value: T public var value: T
get() = @Suppress("UNCHECKED_CAST")(getImpl() as T) get() = @Suppress("UNCHECKED_CAST")(getImpl() as T)
set(new) { set(new) {
if (this.isFrozen) if (this.isShareable())
setImpl(new) setImpl(new)
else else
value_ = new value_ = new
@@ -342,7 +342,9 @@ public class FreezableAtomicReference<T>(private var value_: T) {
* @return the old value * @return the old value
*/ */
public fun compareAndSwap(expected: T, new: T): T { public fun compareAndSwap(expected: T, new: T): T {
return if (this.isFrozen) @Suppress("UNCHECKED_CAST")(compareAndSwapImpl(expected, new) as T) else { return if (this.isShareable()) {
@Suppress("UNCHECKED_CAST")(compareAndSwapImpl(expected, new) as T)
} else {
val old = value_ val old = value_
if (old === expected) value_ = new if (old === expected) value_ = new
old old
@@ -358,7 +360,8 @@ public class FreezableAtomicReference<T>(private var value_: T) {
* @return true if successful * @return true if successful
*/ */
public fun compareAndSet(expected: T, new: T): Boolean { public fun compareAndSet(expected: T, new: T): Boolean {
if (this.isFrozen) return compareAndSetImpl(expected, new) if (this.isShareable())
return compareAndSetImpl(expected, new)
val old = value_ val old = value_
if (old === expected) { if (old === expected) {
value_ = new value_ = new
@@ -14,6 +14,9 @@ import kotlin.native.identityHashCode
import kotlin.reflect.KClass import kotlin.reflect.KClass
import kotlinx.cinterop.* import kotlinx.cinterop.*
@GCUnsafeCall("Kotlin_Any_isShareable")
external internal fun Any?.isShareable(): Boolean
// Implementation details. // Implementation details.
@GCUnsafeCall("Kotlin_Worker_stateOfFuture") @GCUnsafeCall("Kotlin_Worker_stateOfFuture")
@@ -125,8 +125,5 @@ private class CleanerImpl(
private val cleanPtr: NativePtr, private val cleanPtr: NativePtr,
): Cleaner {} ): Cleaner {}
@GCUnsafeCall("Kotlin_Any_isShareable")
external private fun Any?.isShareable(): Boolean
@GCUnsafeCall("CreateStablePointer") @GCUnsafeCall("CreateStablePointer")
external private fun createStablePointer(obj: Any): NativePtr external private fun createStablePointer(obj: Any): NativePtr
+2 -1
View File
@@ -82,7 +82,8 @@ void ObjHeader::destroyMetaObject(ObjHeader* object) {
} }
ALWAYS_INLINE bool isPermanentOrFrozen(const ObjHeader* obj) { ALWAYS_INLINE bool isPermanentOrFrozen(const ObjHeader* obj) {
return mm::IsFrozen(obj); // TODO: Freeze TF_IMMUTABLE objects upon creation.
return mm::IsFrozen(obj) || ((obj->type_info()->flags_ & TF_IMMUTABLE) != 0);
} }
ALWAYS_INLINE bool isShareable(const ObjHeader* obj) { ALWAYS_INLINE bool isShareable(const ObjHeader* obj) {