Make lazy freezing-aware. (#1878)

This commit is contained in:
Nikolay Igotti
2018-08-14 15:45:10 +03:00
committed by GitHub
parent 79755d8fbf
commit 061ea53853
4 changed files with 126 additions and 8 deletions
+48 -5
View File
@@ -18,12 +18,16 @@ object Immutable2 {
}
}
@Test fun runTest() {
assertEquals(42, Immutable.x)
val COUNT = 5
val workers = Array(COUNT, { _ -> startWorker()})
object Immutable3 {
val x by lazy {
var result = 0
for (i in 1 .. 1000)
result += i
result
}
}
fun testSingleData(workers: Array<Worker>) {
val set = mutableSetOf<Any?>()
for (attempt in 1 .. 3) {
val futures = Array(workers.size, { workerIndex ->
@@ -33,6 +37,45 @@ object Immutable2 {
}
assertEquals(set.size, 1)
assertEquals(set.single(), Immutable2.y)
}
fun testFrozenLazy(workers: Array<Worker>) {
val set = mutableSetOf<Int>()
for (attempt in 1 .. 3) {
val futures = Array(workers.size, { workerIndex ->
workers[workerIndex].schedule(TransferMode.CHECKED, { "" }) { _ -> Immutable3.x }
})
futures.forEach { set += it.result() }
}
assertEquals(1, set.size)
assertEquals(Immutable3.x, set.single())
assertEquals(1001 * 500, set.single())
}
fun testLiquidLazy() {
class L {
val value by lazy {
17
}
}
val l1 = L()
for (i in 1 .. 100)
assertEquals(l1.value, 17)
val l2 = L()
l2.freeze()
for (i in 1 .. 100)
assertEquals(l2.value, 17)
}
@Test fun runTest() {
assertEquals(42, Immutable.x)
val COUNT = 5
val workers = Array(COUNT, { _ -> startWorker()})
testSingleData(workers)
testFrozenLazy(workers)
testLiquidLazy()
println("OK")
}
+2 -2
View File
@@ -28,7 +28,7 @@ import kotlin.reflect.KProperty
* the returned instance as it may cause accidental deadlock. Also this behavior can be changed in the future.
*/
@FixmeConcurrency
public actual fun <T> lazy(initializer: () -> T): Lazy<T> = UnsafeLazyImpl(initializer)//SynchronizedLazyImpl(initializer)
public actual fun <T> lazy(initializer: () -> T): Lazy<T> = kotlin.native.worker.FreezeAwareLazyImpl(initializer)
/**
* Creates a new instance of the [Lazy] that uses the specified initialization function [initializer]
@@ -61,4 +61,4 @@ public actual fun <T> lazy(mode: LazyThreadSafetyMode, initializer: () -> T): La
*/
@FixmeConcurrency
@Suppress("UNUSED_PARAMETER")
public actual fun <T> lazy(lock: Any?, initializer: () -> T): Lazy<T> = TODO()//SynchronizedLazyImpl(initializer, lock)
public actual fun <T> lazy(lock: Any?, initializer: () -> T): Lazy<T> = TODO()//SynchronizedLazyImpl(initializer, lock)
@@ -179,7 +179,7 @@ internal class AtomicLazyImpl<out T>(initializer: () -> T) : Lazy<T> {
result = value_.get()
} while (result === INITIALIZING)
assert(result !== UNINITIALIZED && result != INITIALIZING)
assert(result !== UNINITIALIZED && result !== INITIALIZING)
@Suppress("UNCHECKED_CAST")
return result as T
}
@@ -0,0 +1,75 @@
/*
* Copyright 2010-2018 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package kotlin.native.worker
internal class FreezeAwareLazyImpl<out T>(initializer: () -> T) : Lazy<T> {
private var initializer_: (() -> T)? = initializer
private var value_: Any? = UNINITIALIZED
// Objects are not frozen by default on single-threaded targets, shall they?
private val valueFrozen_ = AtomicReference<Any?>(UNINITIALIZED.freeze())
override val value: T
get() {
var result: Any? = value_
if (isFrozen) {
if (result !== UNINITIALIZED) {
assert(result !== INITIALIZING)
@Suppress("UNCHECKED_CAST")
return result as T
}
// Barrier.
// Note that recursive lazy initializers will hang here.
do {
result = valueFrozen_.get()
} while (result === INITIALIZING)
if (result !== UNINITIALIZED) {
@Suppress("UNCHECKED_CAST")
return result as T
}
// TODO: maybe release initializer in frozen case.
if (valueFrozen_.compareAndSwap(UNINITIALIZED, INITIALIZING) === UNINITIALIZED) {
result = initializer_!!().freeze()
val old = valueFrozen_.compareAndSwap(INITIALIZING, result)
assert(old === INITIALIZING)
} else {
do {
result = valueFrozen_.get()
} while (result === INITIALIZING)
}
assert(result !== UNINITIALIZED && result !== INITIALIZING)
@Suppress("UNCHECKED_CAST")
return result as T
} else {
if (result === UNINITIALIZED) {
result = initializer_!!()
if (isFrozen)
throw InvalidMutabilityException(this)
value_ = result
initializer_ = null
}
@Suppress("UNCHECKED_CAST")
return result as T
}
}
// Racy!
override fun isInitialized(): Boolean = (value_ !== UNINITIALIZED) || (valueFrozen_.get() !== UNINITIALIZED)
override fun toString(): String = if (isInitialized())
value.toString() else "Lazy value not initialized yet."
}