Fix race in Lazy for the new MM
This commit is contained in:
committed by
Space
parent
455625bcee
commit
092750e215
@@ -1064,6 +1064,11 @@ standaloneTest("lazy3") {
|
||||
source = "runtime/workers/lazy3.kt"
|
||||
}
|
||||
|
||||
task lazy4(type: KonanLocalTest) {
|
||||
enabled = (project.testTarget != 'wasm32') // Workers need pthreads. Need exceptions
|
||||
source = "runtime/workers/lazy4.kt"
|
||||
}
|
||||
|
||||
task mutableData1(type: KonanLocalTest) {
|
||||
enabled = (project.testTarget != 'wasm32') // Workers need pthreads. Need exceptions
|
||||
source = "runtime/workers/mutableData1.kt"
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.
|
||||
*/
|
||||
|
||||
package runtime.workers.lazy4
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
import kotlin.native.concurrent.*
|
||||
|
||||
const val WORKERS_COUNT = 20
|
||||
|
||||
class C(private val initializer: () -> Int) {
|
||||
val data by lazy { initializer() }
|
||||
}
|
||||
|
||||
fun concurrentLazyAccess(freeze: Boolean) {
|
||||
val initializerCallCount = AtomicInt(0)
|
||||
|
||||
val c = C {
|
||||
initializerCallCount.increment()
|
||||
42
|
||||
}
|
||||
if (freeze) {
|
||||
c.freeze()
|
||||
}
|
||||
|
||||
val workers = Array(WORKERS_COUNT, { Worker.start() })
|
||||
val inited = AtomicInt(0)
|
||||
val canStart = AtomicInt(0)
|
||||
val futures = Array(workers.size) { i ->
|
||||
workers[i].execute(TransferMode.SAFE, { Triple(inited, canStart, c) }) { (inited, canStart, c) ->
|
||||
inited.increment()
|
||||
while (canStart.value != 1) {}
|
||||
c.data
|
||||
}
|
||||
}
|
||||
|
||||
while (inited.value < workers.size) {}
|
||||
canStart.value = 1
|
||||
|
||||
futures.forEach {
|
||||
assertEquals(42, it.result)
|
||||
}
|
||||
workers.forEach {
|
||||
it.requestTermination().result
|
||||
}
|
||||
|
||||
assertEquals(1, initializerCallCount.value)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun concurrentLazyAccessUnfrozen() {
|
||||
if (Platform.memoryModel != MemoryModel.EXPERIMENTAL) {
|
||||
return
|
||||
}
|
||||
concurrentLazyAccess(false)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun concurrentLazyAccessFrozen() {
|
||||
concurrentLazyAccess(true)
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ import kotlin.native.internal.Frozen
|
||||
|
||||
internal class FreezeAwareLazyImpl<out T>(initializer: () -> T) : Lazy<T> {
|
||||
private val value_ = FreezableAtomicReference<Any?>(UNINITIALIZED)
|
||||
// This cannot be made atomic because of the legacy MM. See https://github.com/JetBrains/kotlin-native/pull/3944
|
||||
// So it must be protected by the lock below.
|
||||
private var initializer_: (() -> T)? = initializer
|
||||
private val lock_ = Lock()
|
||||
|
||||
@@ -46,9 +48,10 @@ internal class FreezeAwareLazyImpl<out T>(initializer: () -> T) : Lazy<T> {
|
||||
|
||||
override val value: T
|
||||
get() {
|
||||
return if (isFrozen) {
|
||||
return if (isShareable()) {
|
||||
// TODO: This is probably a big performance problem for lazy with the new MM. Address it.
|
||||
locked(lock_) {
|
||||
getOrInit(true)
|
||||
getOrInit(isFrozen)
|
||||
}
|
||||
} else {
|
||||
getOrInit(false)
|
||||
|
||||
Reference in New Issue
Block a user