[K/N] Implement Lazy for new mm

This commit is contained in:
Pavel Kunyavskiy
2021-07-12 11:51:52 +03:00
committed by Space
parent 2f2e608502
commit d88a665fa8
4 changed files with 168 additions and 35 deletions
@@ -5,9 +5,10 @@
package kotlin
import kotlin.native.concurrent.FreezeAwareLazyImpl
import kotlin.native.concurrent.*
import kotlin.native.internal.FixmeConcurrency
import kotlin.reflect.KProperty
import kotlin.native.isExperimentalMM
/**
* Creates a new instance of the [Lazy] that uses the specified initialization function [initializer]
@@ -18,7 +19,13 @@ import kotlin.reflect.KProperty
* Note that the returned instance uses itself to synchronize on. Do not synchronize from external code on
* the returned instance as it may cause accidental deadlock. Also this behavior can be changed in the future.
*/
public actual fun <T> lazy(initializer: () -> T): Lazy<T> = FreezeAwareLazyImpl(initializer)
@OptIn(kotlin.ExperimentalStdlibApi::class)
public actual fun <T> lazy(initializer: () -> T): Lazy<T> =
if (isExperimentalMM())
SynchronizedLazyImpl(initializer)
else
FreezeAwareLazyImpl(initializer)
/**
* Creates a new instance of the [Lazy] that uses the specified initialization function [initializer]
@@ -31,10 +38,11 @@ public actual fun <T> lazy(initializer: () -> T): Lazy<T> = FreezeAwareLazyImpl(
* Also this behavior can be changed in the future.
*/
@FixmeConcurrency
@OptIn(kotlin.ExperimentalStdlibApi::class)
public actual fun <T> lazy(mode: LazyThreadSafetyMode, initializer: () -> T): Lazy<T> =
when (mode) {
LazyThreadSafetyMode.SYNCHRONIZED -> throw UnsupportedOperationException()
LazyThreadSafetyMode.PUBLICATION -> FreezeAwareLazyImpl(initializer)
LazyThreadSafetyMode.SYNCHRONIZED -> if (isExperimentalMM()) SynchronizedLazyImpl(initializer) else throw UnsupportedOperationException()
LazyThreadSafetyMode.PUBLICATION -> if (isExperimentalMM()) SafePublicationLazyImpl(initializer) else FreezeAwareLazyImpl(initializer)
LazyThreadSafetyMode.NONE -> UnsafeLazyImpl(initializer)
}
@@ -121,3 +121,78 @@ internal class AtomicLazyImpl<out T>(initializer: () -> T) : Lazy<T> {
* such as object signletons, or in cases where it's guaranteed not to have cyclical garbage.
*/
public fun <T> atomicLazy(initializer: () -> T): Lazy<T> = AtomicLazyImpl(initializer)
@Suppress("UNCHECKED_CAST")
internal class SynchronizedLazyImpl<out T>(initializer: () -> T) : Lazy<T> {
private var initializer = FreezableAtomicReference<(() -> T)?>(initializer)
private var valueRef = FreezableAtomicReference<Any?>(UNINITIALIZED)
private val lock = Lock()
override val value: T
get() {
val _v1 = valueRef.value
if (_v1 !== UNINITIALIZED) {
return _v1 as T
}
return locked(lock) {
val _v2 = valueRef.value
if (_v2 === UNINITIALIZED) {
val wasFrozen = this.isFrozen
val typedValue = initializer.value!!()
if (this.isFrozen) {
if (!wasFrozen) {
throw InvalidMutabilityException("Frozen during lazy computation")
}
typedValue.freeze()
}
valueRef.value = typedValue
initializer.value = null
typedValue
} else {
_v2 as T
}
}
}
override fun isInitialized() = valueRef.value !== UNINITIALIZED
override fun toString(): String = if (isInitialized()) value.toString() else "Lazy value not initialized yet."
}
@Suppress("UNCHECKED_CAST")
internal class SafePublicationLazyImpl<out T>(initializer: () -> T) : Lazy<T> {
private var initializer = FreezableAtomicReference<(() -> T)?>(initializer)
private var valueRef = FreezableAtomicReference<Any?>(UNINITIALIZED)
override val value: T
get() {
val value = valueRef.value
if (value !== UNINITIALIZED) {
return value as T
}
val initializerValue = initializer.value
// if we see null in initializer here, it means that the value is already set by another thread
if (initializerValue != null) {
val wasFrozen = this.isFrozen
val newValue = initializerValue()
if (this.isFrozen) {
if (!wasFrozen) {
throw InvalidMutabilityException("Frozen during lazy computation")
}
newValue.freeze()
}
if (valueRef.compareAndSet(UNINITIALIZED, newValue)) {
initializer.value = null
return newValue
}
}
return valueRef.value as T
}
override fun isInitialized(): Boolean = valueRef.value !== UNINITIALIZED
override fun toString(): String = if (isInitialized()) value.toString() else "Lazy value not initialized yet."
}