diff --git a/kotlin-native/backend.native/tests/objcexport/values.swift b/kotlin-native/backend.native/tests/objcexport/values.swift index 297611283b1..6f1bd508216 100644 --- a/kotlin-native/backend.native/tests/objcexport/values.swift +++ b/kotlin-native/backend.native/tests/objcexport/values.swift @@ -596,17 +596,10 @@ func testShared() throws { try assertFalse(ValuesKt.isFrozen(obj: obj), "isFrozen(\(obj))") } - if ValuesKt.isExperimentalMM { - try assertNotFrozen(NSObject()) - try assertNotFrozen(TestSharedIImpl()) - try assertNotFrozen(ValuesKt.kotlinLambda(block: { return $0 }) as AnyObject) - try assertNotFrozen(FinalClassExtOpen()) - } else { - try assertFrozen(NSObject()) - try assertFrozen(TestSharedIImpl()) - try assertFrozen(ValuesKt.kotlinLambda(block: { return $0 }) as AnyObject) - try assertNotFrozen(FinalClassExtOpen()) - } + try assertFrozen(NSObject()) + try assertFrozen(TestSharedIImpl()) + try assertFrozen(ValuesKt.kotlinLambda(block: { return $0 }) as AnyObject) + try assertNotFrozen(FinalClassExtOpen()) } class PureSwiftClass { diff --git a/kotlin-native/backend.native/tests/runtime/workers/freeze2.kt b/kotlin-native/backend.native/tests/runtime/workers/freeze2.kt index 0bb2be40ef4..7fb53594c04 100644 --- a/kotlin-native/backend.native/tests/runtime/workers/freeze2.kt +++ b/kotlin-native/backend.native/tests/runtime/workers/freeze2.kt @@ -59,36 +59,27 @@ data class Data(var int: Int) assertFailsWith { a8[1] = 2.0 } // 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() var data: Any = "Hello" + " " + "world" - if (hasToBeFrozen) { - assertTrue(data.isFrozen) - } + assertTrue(data.isFrozen) worker.execute(TransferMode.SAFE, { data } ) { input -> println("Worker 1: $input") }.result data = 42 - if (hasToBeFrozen) { - assertTrue(data.isFrozen) - } + assertTrue(data.isFrozen) worker.execute(TransferMode.SAFE, { data } ) { input -> println("Worker2: $input") }.result data = 239.0 - if (hasToBeFrozen) { - assertTrue(data.isFrozen) - } + assertTrue(data.isFrozen) worker.execute(TransferMode.SAFE, { data } ) { input -> println("Worker3: $input") }.result data = 'a' - if (hasToBeFrozen) { - assertTrue(data.isFrozen) - } + assertTrue(data.isFrozen) worker.execute(TransferMode.SAFE, { data } ) { input -> println("Worker4: $input") }.result diff --git a/kotlin-native/runtime/src/main/cpp/Atomic.cpp b/kotlin-native/runtime/src/main/cpp/Atomic.cpp index 5f059b8a956..f1f550fd83d 100644 --- a/kotlin-native/runtime/src/main/cpp/Atomic.cpp +++ b/kotlin-native/runtime/src/main/cpp/Atomic.cpp @@ -182,7 +182,9 @@ void Kotlin_AtomicReference_checkIfFrozen(KRef value) { } 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. AtomicReferenceLayout* ref = asAtomicReference(thiz); 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) { - Kotlin_AtomicReference_checkIfFrozen(newValue); + if (isPermanentOrFrozen(thiz)) { + Kotlin_AtomicReference_checkIfFrozen(newValue); + } // See Kotlin_AtomicReference_get() for explanations, why locking is needed. AtomicReferenceLayout* ref = asAtomicReference(thiz); ObjHolder holder; @@ -200,7 +204,9 @@ KBoolean Kotlin_AtomicReference_compareAndSet(KRef thiz, KRef expectedValue, KRe } void Kotlin_AtomicReference_set(KRef thiz, KRef newValue) { - Kotlin_AtomicReference_checkIfFrozen(newValue); + if (isPermanentOrFrozen(thiz)) { + Kotlin_AtomicReference_checkIfFrozen(newValue); + } AtomicReferenceLayout* ref = asAtomicReference(thiz); SetHeapRefLocked(&ref->value_, newValue, &ref->lock_, &ref->cookie_); } diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/native/concurrent/Atomics.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/native/concurrent/Atomics.kt index 62605702847..e988df5ccec 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/native/concurrent/Atomics.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/native/concurrent/Atomics.kt @@ -326,7 +326,7 @@ public class FreezableAtomicReference(private var value_: T) { public var value: T get() = @Suppress("UNCHECKED_CAST")(getImpl() as T) set(new) { - if (this.isFrozen) + if (this.isShareable()) setImpl(new) else value_ = new @@ -342,7 +342,9 @@ public class FreezableAtomicReference(private var value_: T) { * @return the old value */ 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_ if (old === expected) value_ = new old @@ -358,7 +360,8 @@ public class FreezableAtomicReference(private var value_: T) { * @return true if successful */ 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_ if (old === expected) { value_ = new diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/native/concurrent/Internal.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/native/concurrent/Internal.kt index 302e01a2418..d54ab2818ec 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/native/concurrent/Internal.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/native/concurrent/Internal.kt @@ -14,6 +14,9 @@ import kotlin.native.identityHashCode import kotlin.reflect.KClass import kotlinx.cinterop.* +@GCUnsafeCall("Kotlin_Any_isShareable") +external internal fun Any?.isShareable(): Boolean + // Implementation details. @GCUnsafeCall("Kotlin_Worker_stateOfFuture") diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/Cleaner.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/Cleaner.kt index fbeed01a169..59799524924 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/Cleaner.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/Cleaner.kt @@ -125,8 +125,5 @@ private class CleanerImpl( private val cleanPtr: NativePtr, ): Cleaner {} -@GCUnsafeCall("Kotlin_Any_isShareable") -external private fun Any?.isShareable(): Boolean - @GCUnsafeCall("CreateStablePointer") external private fun createStablePointer(obj: Any): NativePtr diff --git a/kotlin-native/runtime/src/mm/cpp/Memory.cpp b/kotlin-native/runtime/src/mm/cpp/Memory.cpp index cc3b253c31e..98c4f49fcd4 100644 --- a/kotlin-native/runtime/src/mm/cpp/Memory.cpp +++ b/kotlin-native/runtime/src/mm/cpp/Memory.cpp @@ -82,7 +82,8 @@ void ObjHeader::destroyMetaObject(ObjHeader* object) { } 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) {