Skip to content
This repository has been archived by the owner on Aug 10, 2021. It is now read-only.

Store initializing singletons in a separate thread local small map #4019

Merged
merged 8 commits into from
Mar 27, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Add a test
  • Loading branch information
projedi committed Mar 24, 2020
commit a5abaae70f4b4b783e5319f306ad3638120d8e4a
5 changes: 5 additions & 0 deletions backend.native/tests/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2675,6 +2675,11 @@ task initializers5(type: KonanLocalTest) {
source = "runtime/basic/initializers5.kt"
}

task initializers6(type: KonanLocalTest) {
disabled = project.testTarget == 'wasm32' // Needs workers.
source = "runtime/basic/initializers6.kt"
}

task expression_as_statement(type: KonanLocalTest) {
expectedFail = (project.testTarget == 'wasm32') // uses exceptions.
goldValue = "Ok\n"
Expand Down
75 changes: 75 additions & 0 deletions backend.native/tests/runtime/basic/initializers6.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* 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.basic.initializers6

import kotlin.test.*

import kotlin.native.concurrent.*

val aWorkerId = 2
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if this is always correct.

val bWorkersCount = 3

val aWorkerUnlocker = AtomicInt(0)
val bWorkerUnlocker = AtomicInt(0)

object A {
init {
// Must be called by aWorker only.
assertEquals(aWorkerId, Worker.current.id)
// Only allow b workers to run, when a worker has started initialization.
bWorkerUnlocker.increment()
// Only proceed with initialization, when all b workers have started executing.
while (aWorkerUnlocker.value < bWorkersCount) {}
// And now wait a bit, to increase probability of races.
Worker.current.park(1000 * 1000L)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

100 ms would be enough :)

}
val a = produceA()
val b = produceB()
}

fun produceA(): String {
// Must've been called by aWorker only.
assertEquals(aWorkerId, Worker.current.id)
return "A"
}

fun produceB(): String {
// Must've been called by aWorker only.
assertEquals(aWorkerId, Worker.current.id)
// Also check that it's ok to get A.a while initializing A.b.
return "B+${A.a}"
}

@Test fun runTest() {
val aWorker = Worker.start()
// This test relies on aWorkerId value.
assertEquals(aWorkerId, aWorker.id)
val bWorkers = Array(bWorkersCount, { _ -> Worker.start() })

val aFuture = aWorker.execute(TransferMode.SAFE, {}, {
A.b
})
val bFutures = Array(bWorkers.size, {
bWorkers[it].execute(TransferMode.SAFE, {}, {
// Wait until A has started to initialize.
while (bWorkerUnlocker.value < 1) {}
// Now allow A initialization to continue.
aWorkerUnlocker.increment()
// And this should not've tried to init A itself.
A.a + A.b
})
})

for (future in bFutures) {
assertEquals("AB+A", future.result)
}
assertEquals("B+A", aFuture.result)

for (worker in bWorkers) {
worker.requestTermination().result
}
aWorker.requestTermination().result
}