diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index a51bad5f035..aae5db61fb9 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -1040,6 +1040,10 @@ standaloneTest("lazy2") { source = "runtime/workers/lazy2.kt" } +standaloneTest("lazy3") { + source = "runtime/workers/lazy3.kt" +} + task enumIdentity(type: KonanLocalTest) { enabled = (project.testTarget != 'wasm32') // Workers need pthreads. goldValue = "true\n" diff --git a/backend.native/tests/runtime/workers/lazy3.kt b/backend.native/tests/runtime/workers/lazy3.kt new file mode 100644 index 00000000000..308df46ee15 --- /dev/null +++ b/backend.native/tests/runtime/workers/lazy3.kt @@ -0,0 +1,54 @@ +import kotlin.native.concurrent.* +import kotlin.native.ref.* +import kotlin.test.* + +fun main() { + test1() + test2() +} + +fun test1() { + ensureGetsCollectedFrozenAndNotFrozen { LazyCapturesThis() } + ensureGetsCollectedFrozenAndNotFrozen { + val l = LazyCapturesThis() + l.bar + l + } + ensureGetsCollected { + val l = LazyCapturesThis().freeze() + l.bar + l + } +} + +class LazyCapturesThis { + fun foo() = 42 + val bar by lazy { foo() } +} + +fun test2() { + ensureGetsCollectedFrozenAndNotFrozen { Throwable() } + ensureGetsCollectedFrozenAndNotFrozen { + val throwable = Throwable() + throwable.getStackTrace() + throwable + } + ensureGetsCollected { + val throwable = Throwable().freeze() + throwable.getStackTrace() + throwable + } +} + +fun ensureGetsCollectedFrozenAndNotFrozen(create: () -> Any) { + ensureGetsCollected { create().freeze() } + ensureGetsCollected(create) +} + +fun ensureGetsCollected(create: () -> Any) { + val ref = makeWeakRef(create) + kotlin.native.internal.GC.collect() + assertNull(ref.get()) +} + +fun makeWeakRef(create: () -> Any) = WeakReference(create()) \ No newline at end of file diff --git a/runtime/src/main/kotlin/kotlin/native/concurrent/Lazy.kt b/runtime/src/main/kotlin/kotlin/native/concurrent/Lazy.kt index e05d4fd0b5e..767d8d2f17c 100644 --- a/runtime/src/main/kotlin/kotlin/native/concurrent/Lazy.kt +++ b/runtime/src/main/kotlin/kotlin/native/concurrent/Lazy.kt @@ -9,7 +9,7 @@ import kotlin.native.internal.Frozen internal class FreezeAwareLazyImpl(initializer: () -> T) : Lazy { private val value_ = FreezableAtomicReference(UNINITIALIZED) - private val initializer_ = FreezableAtomicReference<(() -> T)?>(initializer) + private var initializer_: (() -> T)? = initializer private val lock_ = Lock() private fun getOrInit(doFreeze: Boolean): T { @@ -25,20 +25,22 @@ internal class FreezeAwareLazyImpl(initializer: () -> T) : Lazy { // Set value_ to INITIALIZING. value_.value = INITIALIZING try { - result = initializer_.value!!() + result = initializer_!!() if (doFreeze) result.freeze() } catch (throwable: Throwable) { value_.value = UNINITIALIZED throw throwable } - if (!doFreeze && this.isFrozen) { - value_.value = UNINITIALIZED - throw InvalidMutabilityException("Frozen during lazy computation") + if (!doFreeze) { + if (this.isFrozen) { + value_.value = UNINITIALIZED + throw InvalidMutabilityException("Frozen during lazy computation") + } + // Clear initializer. + initializer_ = null } // Set value_ to actual one. value_.value = result - // Clear initializer. - initializer_.value = null @Suppress("UNCHECKED_CAST") return result as T }