From 101e7f0048051d4f74746f1d35b7ab07841d1794 Mon Sep 17 00:00:00 2001 From: Nikolay Igotti Date: Fri, 7 Sep 2018 16:30:01 +0300 Subject: [PATCH] Fix memory bug induced by frozen lazy. (#2018) --- backend.native/tests/build.gradle | 5 +++ backend.native/tests/runtime/workers/lazy2.kt | 31 +++++++++++++++++++ .../kotlin/kotlin/native/concurrent/Lazy.kt | 5 +-- 3 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 backend.native/tests/runtime/workers/lazy2.kt diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index e24c6b64f1d..1a65f25d31a 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -713,6 +713,11 @@ task lazy1(type: RunKonanTest) { source = "runtime/workers/lazy1.kt" } +task lazy2(type: RunStandaloneKonanTest) { + goldValue = "123\nOK\n" + source = "runtime/workers/lazy2.kt" +} + task enumIdentity(type: RunKonanTest) { disabled = (project.testTarget == 'wasm32') // Workers need pthreads. goldValue = "true\n" diff --git a/backend.native/tests/runtime/workers/lazy2.kt b/backend.native/tests/runtime/workers/lazy2.kt new file mode 100644 index 00000000000..49d08ae9957 --- /dev/null +++ b/backend.native/tests/runtime/workers/lazy2.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ +import kotlin.test.* + +object Foo { + val bar = Bar() +} + +class Bar { + val f by lazy { + foo() + } + + fun foo() = 123 +} + +fun printAll() { + println(Foo.bar.f) +} + +// This test is extracted from the real problem found in kotlinx.serialization, where zeroing out +// initializer field in frozen lazy object led to the crash, induced by breaking frozen objects' +// invariant (initializer end up in the same container as the lazy object itself, so it was destroyed +// earlier than it should when reference counter was decremented). +fun main(args: Array) { + printAll() + kotlin.native.internal.GC.collect() + println("OK") +} \ 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 112d6f4cc60..4e743e01138 100644 --- a/runtime/src/main/kotlin/kotlin/native/concurrent/Lazy.kt +++ b/runtime/src/main/kotlin/kotlin/native/concurrent/Lazy.kt @@ -36,8 +36,9 @@ internal class FreezeAwareLazyImpl(initializer: () -> T) : Lazy { if (!ensureAcyclicAndSet(this, 0, result)) { throw InvalidMutabilityException("Setting cyclic data via lazy in $this: $result") } - // Clear initializer_ reference. - ensureAcyclicAndSet(this, 1, null) + // Do not clear initializer_ reference, as it may break freezing invariants and zero out + // still valid object. It seems to be safe only in case when `this` is not reachable from + // initializer. @Suppress("UNCHECKED_CAST") return result as T }