Fix lazy {} memory leak regression caused by #3862
This commit is contained in:
committed by
GitHub
parent
33f9f9b917
commit
53022fac12
@@ -1040,6 +1040,10 @@ standaloneTest("lazy2") {
|
|||||||
source = "runtime/workers/lazy2.kt"
|
source = "runtime/workers/lazy2.kt"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
standaloneTest("lazy3") {
|
||||||
|
source = "runtime/workers/lazy3.kt"
|
||||||
|
}
|
||||||
|
|
||||||
task enumIdentity(type: KonanLocalTest) {
|
task enumIdentity(type: KonanLocalTest) {
|
||||||
enabled = (project.testTarget != 'wasm32') // Workers need pthreads.
|
enabled = (project.testTarget != 'wasm32') // Workers need pthreads.
|
||||||
goldValue = "true\n"
|
goldValue = "true\n"
|
||||||
|
|||||||
@@ -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())
|
||||||
@@ -9,7 +9,7 @@ import kotlin.native.internal.Frozen
|
|||||||
|
|
||||||
internal class FreezeAwareLazyImpl<out T>(initializer: () -> T) : Lazy<T> {
|
internal class FreezeAwareLazyImpl<out T>(initializer: () -> T) : Lazy<T> {
|
||||||
private val value_ = FreezableAtomicReference<Any?>(UNINITIALIZED)
|
private val value_ = FreezableAtomicReference<Any?>(UNINITIALIZED)
|
||||||
private val initializer_ = FreezableAtomicReference<(() -> T)?>(initializer)
|
private var initializer_: (() -> T)? = initializer
|
||||||
private val lock_ = Lock()
|
private val lock_ = Lock()
|
||||||
|
|
||||||
private fun getOrInit(doFreeze: Boolean): T {
|
private fun getOrInit(doFreeze: Boolean): T {
|
||||||
@@ -25,20 +25,22 @@ internal class FreezeAwareLazyImpl<out T>(initializer: () -> T) : Lazy<T> {
|
|||||||
// Set value_ to INITIALIZING.
|
// Set value_ to INITIALIZING.
|
||||||
value_.value = INITIALIZING
|
value_.value = INITIALIZING
|
||||||
try {
|
try {
|
||||||
result = initializer_.value!!()
|
result = initializer_!!()
|
||||||
if (doFreeze) result.freeze()
|
if (doFreeze) result.freeze()
|
||||||
} catch (throwable: Throwable) {
|
} catch (throwable: Throwable) {
|
||||||
value_.value = UNINITIALIZED
|
value_.value = UNINITIALIZED
|
||||||
throw throwable
|
throw throwable
|
||||||
}
|
}
|
||||||
if (!doFreeze && this.isFrozen) {
|
if (!doFreeze) {
|
||||||
value_.value = UNINITIALIZED
|
if (this.isFrozen) {
|
||||||
throw InvalidMutabilityException("Frozen during lazy computation")
|
value_.value = UNINITIALIZED
|
||||||
|
throw InvalidMutabilityException("Frozen during lazy computation")
|
||||||
|
}
|
||||||
|
// Clear initializer.
|
||||||
|
initializer_ = null
|
||||||
}
|
}
|
||||||
// Set value_ to actual one.
|
// Set value_ to actual one.
|
||||||
value_.value = result
|
value_.value = result
|
||||||
// Clear initializer.
|
|
||||||
initializer_.value = null
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
return result as T
|
return result as T
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user