Provide lazy implementation with an external object to synchronize on.
This commit is contained in:
@@ -62,6 +62,12 @@ public fun lazy<T>(initializer: () -> T): Lazy<T> = UnsafeLazyImpl(initializer)
|
||||
* The [mode] parameter is ignored. */
|
||||
public fun lazy<T>(mode: LazyThreadSafetyMode, initializer: () -> T): Lazy<T> = UnsafeLazyImpl(initializer)
|
||||
|
||||
/**
|
||||
* Creates a new instance of the [Lazy] that uses the specified initialization function [initializer].
|
||||
*
|
||||
* The [lock] parameter is ignored.
|
||||
*/
|
||||
public fun lazy<T>(lock: Any?, initializer: () -> T): Lazy<T> = UnsafeLazyImpl(initializer)
|
||||
|
||||
private fun arrayCopyResize(source: dynamic, newSize: Int, defaultValue: Any?): dynamic {
|
||||
val result = source.slice(0, newSize)
|
||||
|
||||
@@ -30,7 +30,7 @@ public object Delegates {
|
||||
* the property delegate object itself is used as a lock.
|
||||
* @param initializer the function that returns the value of the property.
|
||||
*/
|
||||
deprecated("Use lazy {} instead in kotlin package")
|
||||
deprecated("Use lazy(lock) {} instead in kotlin package", ReplaceWith("lazy(lock, initializer)"))
|
||||
public fun blockingLazy<T>(lock: Any?, initializer: () -> T): ReadOnlyProperty<Any?, T> = BlockingLazyVal(lock, initializer)
|
||||
|
||||
/**
|
||||
|
||||
@@ -51,9 +51,11 @@ public enum class LazyThreadSafetyMode {
|
||||
|
||||
private object UNINITIALIZED_VALUE
|
||||
|
||||
private class LazyImpl<out T>(initializer: () -> T) : Lazy<T>(), Serializable {
|
||||
private open class LazyImpl<out T>(initializer: () -> T) : Lazy<T>(), Serializable {
|
||||
private var initializer: (() -> T)? = initializer
|
||||
private volatile var _value: Any? = UNINITIALIZED_VALUE
|
||||
protected open val lock: Any
|
||||
get() = this
|
||||
|
||||
override val value: T
|
||||
get() {
|
||||
@@ -62,7 +64,7 @@ private class LazyImpl<out T>(initializer: () -> T) : Lazy<T>(), Serializable {
|
||||
return _v1 as T
|
||||
}
|
||||
|
||||
return synchronized(this) {
|
||||
return synchronized(lock) {
|
||||
val _v2 = _value
|
||||
if (_v2 !== UNINITIALIZED_VALUE) {
|
||||
_v2 as T
|
||||
@@ -83,6 +85,7 @@ private class LazyImpl<out T>(initializer: () -> T) : Lazy<T>(), Serializable {
|
||||
private fun writeReplace(): Any = InitializedLazyImpl(value)
|
||||
}
|
||||
|
||||
private class ExternallySynchronizedLazyImpl<out T>(override val lock: Any, initializer: () -> T): LazyImpl<T>(initializer)
|
||||
|
||||
private class UnsafeLazyImpl<out T>(initializer: () -> T) : Lazy<T>(), Serializable {
|
||||
private var initializer: (() -> T)? = initializer
|
||||
|
||||
@@ -27,3 +27,20 @@ public fun lazy<T>(mode: LazyThreadSafetyMode, initializer: () -> T): Lazy<T> =
|
||||
LazyThreadSafetyMode.SYNCHRONIZED -> LazyImpl(initializer)
|
||||
LazyThreadSafetyMode.NONE -> UnsafeLazyImpl(initializer)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of the [Lazy] that uses the specified initialization function [initializer]
|
||||
* and the default thread-safety mode [LazyThreadSafetyMode.SYNCHRONIZED].
|
||||
*
|
||||
* If the initialization of a value throws an exception, it will attempt to reinitialize the value at next access.
|
||||
*
|
||||
* The returned instance uses the specified [lock] object to synchronize on.
|
||||
* When the [lock] is not specified the instance uses itself to synchronize on,
|
||||
* in this case 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 fun lazy<T>(lock: Any?, initializer: () -> T): Lazy<T> =
|
||||
if (lock != null)
|
||||
ExternallySynchronizedLazyImpl(lock, initializer)
|
||||
else
|
||||
LazyImpl(initializer)
|
||||
@@ -17,6 +17,23 @@ class LazyValTest {
|
||||
}
|
||||
}
|
||||
|
||||
class SynchronizedLazyValTest {
|
||||
volatile var result = 0
|
||||
val a by lazy(this) {
|
||||
++result
|
||||
}
|
||||
|
||||
test fun doTest() {
|
||||
synchronized(this) {
|
||||
// thread { a } // not available in js // TODO: Make this test JVM-only
|
||||
result = 1
|
||||
a
|
||||
}
|
||||
assertTrue(a == 2, "fail: initializer should be invoked only once")
|
||||
assertTrue(result == 2, "fail result should be incremented after test")
|
||||
}
|
||||
}
|
||||
|
||||
class UnsafeLazyValTest {
|
||||
var result = 0
|
||||
val a by lazy(LazyThreadSafetyMode.NONE) {
|
||||
|
||||
Reference in New Issue
Block a user