Move everything under kotlin-native folder
I was forced to manually do update the following files, because otherwise they would be ignored according .gitignore settings. Probably they should be deleted from repo. Interop/.idea/compiler.xml Interop/.idea/gradle.xml Interop/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_runtime_1_0_3.xml Interop/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_1_0_3.xml Interop/.idea/modules.xml Interop/.idea/modules/Indexer/Indexer.iml Interop/.idea/modules/Runtime/Runtime.iml Interop/.idea/modules/StubGenerator/StubGenerator.iml backend.native/backend.native.iml backend.native/bc.frontend/bc.frontend.iml backend.native/cli.bc/cli.bc.iml backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.kt backend.native/tests/link/lib/foo.kt backend.native/tests/link/lib/foo2.kt backend.native/tests/teamcity-test.property
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.atomic0
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
import kotlin.native.concurrent.*
|
||||
|
||||
fun test1(workers: Array<Worker>) {
|
||||
val atomic = AtomicInt(15)
|
||||
val futures = Array(workers.size, { workerIndex ->
|
||||
workers[workerIndex].execute(TransferMode.SAFE, { atomic }) {
|
||||
input -> input.addAndGet(1)
|
||||
}
|
||||
})
|
||||
futures.forEach {
|
||||
it.result
|
||||
}
|
||||
println(atomic.value)
|
||||
}
|
||||
|
||||
fun test2(workers: Array<Worker>) {
|
||||
val atomic = AtomicInt(1)
|
||||
val counter = AtomicInt(0)
|
||||
val futures = Array(workers.size, { workerIndex ->
|
||||
workers[workerIndex].execute(TransferMode.SAFE, { Triple(atomic, workerIndex, counter) }) {
|
||||
(place, index, result) ->
|
||||
// Here we simulate mutex using [place] location to store tag of the current worker.
|
||||
// When it is negative - worker executes exclusively.
|
||||
val tag = index + 1
|
||||
while (place.compareAndSwap(tag, -tag) != tag) {}
|
||||
val ok1 = result.addAndGet(1) == index + 1
|
||||
// Now, let the next worker run.
|
||||
val ok2 = place.compareAndSwap(-tag, tag + 1) == -tag
|
||||
ok1 && ok2
|
||||
}
|
||||
})
|
||||
futures.forEach {
|
||||
assertEquals(it.result, true)
|
||||
}
|
||||
println(counter.value)
|
||||
}
|
||||
|
||||
data class Data(val value: Int)
|
||||
|
||||
fun test3(workers: Array<Worker>) {
|
||||
val common = AtomicReference<Data?>(null)
|
||||
val futures = Array(workers.size, { workerIndex ->
|
||||
workers[workerIndex].execute(TransferMode.SAFE, { Pair(common, workerIndex) }) {
|
||||
(place, index) ->
|
||||
val mine = Data(index).freeze()
|
||||
// Try to publish our own data, until successful, in a tight loop.
|
||||
while (!place.compareAndSet(null, mine)) {}
|
||||
}
|
||||
})
|
||||
val seen = mutableSetOf<Data>()
|
||||
for (i in 0 until workers.size) {
|
||||
do {
|
||||
val current = common.value
|
||||
if (current != null && !seen.contains(current)) {
|
||||
seen += current
|
||||
// Let others publish.
|
||||
assertEquals(common.compareAndSwap(current, null), current)
|
||||
break
|
||||
}
|
||||
} while (true)
|
||||
}
|
||||
futures.forEach {
|
||||
it.result
|
||||
}
|
||||
assertEquals(seen.size, workers.size)
|
||||
}
|
||||
|
||||
fun test4() {
|
||||
assertFailsWith<InvalidMutabilityException> {
|
||||
AtomicReference(Data(1))
|
||||
}
|
||||
assertFailsWith<InvalidMutabilityException> {
|
||||
AtomicReference<Data?>(null).compareAndSwap(null, Data(2))
|
||||
}
|
||||
}
|
||||
|
||||
fun test5() {
|
||||
assertFailsWith<InvalidMutabilityException> {
|
||||
AtomicReference<Data?>(null).value = Data(2)
|
||||
}
|
||||
val ref = AtomicReference<Data?>(null)
|
||||
val value = Data(3).freeze()
|
||||
assertEquals(null, ref.value)
|
||||
ref.value = value
|
||||
assertEquals(3, ref.value!!.value)
|
||||
}
|
||||
|
||||
fun test6() {
|
||||
val int = AtomicInt(0)
|
||||
int.value = 239
|
||||
assertEquals(239, int.value)
|
||||
val long = AtomicLong(0)
|
||||
long.value = 239L
|
||||
assertEquals(239L, long.value)
|
||||
}
|
||||
|
||||
|
||||
fun test7() {
|
||||
val ref = FreezableAtomicReference(Array(1) { "hey" })
|
||||
ref.value[0] = "ho"
|
||||
assertEquals(ref.value[0], "ho")
|
||||
ref.value = Array(1) { "po" }
|
||||
assertEquals(ref.value[0], "po")
|
||||
ref.freeze()
|
||||
assertFailsWith<InvalidMutabilityException> {
|
||||
ref.value = Array(1) { "no" }
|
||||
}
|
||||
assertFailsWith<InvalidMutabilityException> {
|
||||
ref.value[0] = "go"
|
||||
}
|
||||
ref.value = Array(1) { "so" }.freeze()
|
||||
assertEquals(ref.value[0], "so")
|
||||
}
|
||||
|
||||
@Test fun runTest() {
|
||||
val COUNT = 20
|
||||
val workers = Array(COUNT, { _ -> Worker.start()})
|
||||
|
||||
test1(workers)
|
||||
test2(workers)
|
||||
test3(workers)
|
||||
test4()
|
||||
test5()
|
||||
test6()
|
||||
test7()
|
||||
|
||||
workers.forEach {
|
||||
it.requestTermination().result
|
||||
}
|
||||
println("OK")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// Note: This test reproduces a race, so it'll start flaking if problem is reintroduced.
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
import kotlin.native.concurrent.*
|
||||
import kotlin.native.internal.*
|
||||
|
||||
val thrashGC = AtomicInt(1)
|
||||
val canStartCreating = AtomicInt(0)
|
||||
val createdCount = AtomicInt(0)
|
||||
val canStartReading = AtomicInt(0)
|
||||
const val atomicsCount = 1000
|
||||
const val workersCount = 10
|
||||
|
||||
fun main() {
|
||||
val gcWorker = Worker.start()
|
||||
val future = gcWorker.execute(TransferMode.SAFE, {}, {
|
||||
canStartCreating.value = 1
|
||||
while (thrashGC.value != 0) {
|
||||
GC.collectCyclic()
|
||||
}
|
||||
GC.collect()
|
||||
})
|
||||
|
||||
while (canStartCreating.value == 0) {}
|
||||
|
||||
val workers = Array(workersCount) { Worker.start() }
|
||||
val futures = workers.map {
|
||||
it.execute(TransferMode.SAFE, {}, {
|
||||
val atomics = Array(atomicsCount) {
|
||||
AtomicReference<Any?>(Any().freeze())
|
||||
}
|
||||
createdCount.increment()
|
||||
while (canStartReading.value == 0) {}
|
||||
GC.collect()
|
||||
atomics.all { it.value != null }
|
||||
})
|
||||
}
|
||||
|
||||
while (createdCount.value != workersCount) {}
|
||||
|
||||
thrashGC.value = 0
|
||||
future.result
|
||||
GC.collect()
|
||||
canStartReading.value = 1
|
||||
|
||||
assertTrue(futures.all { it.result })
|
||||
|
||||
for (worker in workers) {
|
||||
worker.requestTermination().result
|
||||
}
|
||||
gcWorker.requestTermination().result
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.enum_identity
|
||||
|
||||
import kotlin.test.*
|
||||
import kotlin.native.concurrent.*
|
||||
|
||||
enum class A {
|
||||
A, B
|
||||
}
|
||||
|
||||
data class Foo(val kind: A)
|
||||
|
||||
// Enums are shared between threads so identity should be kept.
|
||||
@Test
|
||||
fun runTest() {
|
||||
val result = Worker.start().execute(TransferMode.SAFE, { Foo(A.B) }, { input ->
|
||||
input.kind == A.B
|
||||
}).result
|
||||
println(result)
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.freeze0
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
import kotlin.native.concurrent.*
|
||||
|
||||
data class SharedDataMember(val double: Double)
|
||||
|
||||
data class SharedData(val string: String, val int: Int, val member: SharedDataMember)
|
||||
|
||||
@Test fun runTest() {
|
||||
val worker = Worker.start()
|
||||
// Create immutable shared data.
|
||||
val immutable = SharedData("Hello", 10, SharedDataMember(0.1)).freeze()
|
||||
println("frozen bit is ${immutable.isFrozen}")
|
||||
|
||||
val future = worker.execute(TransferMode.SAFE, { immutable } ) {
|
||||
input ->
|
||||
println("Worker: $input")
|
||||
input
|
||||
}
|
||||
future.consume {
|
||||
result -> println("Main: $result")
|
||||
}
|
||||
worker.requestTermination().result
|
||||
println("OK")
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.freeze1
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
import kotlin.native.concurrent.*
|
||||
|
||||
data class Node(var previous: Node?, var data: Int)
|
||||
|
||||
fun makeCycle(count: Int): Node {
|
||||
val first = Node(null, 0)
|
||||
var current = first
|
||||
for (index in 1 .. count - 1) {
|
||||
current = Node(current, index)
|
||||
}
|
||||
first.previous = current
|
||||
return first
|
||||
}
|
||||
|
||||
data class Node2(var leaf1: Node2?, var leaf2: Node2?)
|
||||
|
||||
fun makeDiamond(): Node2 {
|
||||
val bottom = Node2(null, null)
|
||||
val mid1prime = Node2(bottom, null)
|
||||
val mid1 = Node2(mid1prime, null)
|
||||
val mid2 = Node2(bottom, null)
|
||||
return Node2(mid1, mid2)
|
||||
}
|
||||
|
||||
@Test fun runTest() {
|
||||
makeCycle(10).freeze()
|
||||
|
||||
// Must be able to freeze diamond shaped graph.
|
||||
val diamond = makeDiamond().freeze()
|
||||
|
||||
val immutable = Node(null, 4).freeze()
|
||||
try {
|
||||
immutable.data = 42
|
||||
} catch (e: InvalidMutabilityException) {
|
||||
println("OK, cannot mutate frozen")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.freeze2
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
import kotlin.native.concurrent.*
|
||||
|
||||
data class Data(var int: Int)
|
||||
|
||||
@Test fun runTest() {
|
||||
// Ensure that we can not mutate frozen objects and arrays.
|
||||
val a0 = Data(2)
|
||||
a0.int++
|
||||
a0.freeze()
|
||||
assertFailsWith<InvalidMutabilityException> {a0.int++ }
|
||||
|
||||
val a1 = ByteArray(2)
|
||||
a1[1]++
|
||||
a1.freeze()
|
||||
assertFailsWith<InvalidMutabilityException> { a1[1]++ }
|
||||
|
||||
val a2 = ShortArray(2)
|
||||
a2[1]++
|
||||
a2.freeze()
|
||||
assertFailsWith<InvalidMutabilityException> { a2[1]++ }
|
||||
|
||||
val a3 = IntArray(2)
|
||||
a3[1]++
|
||||
a3.freeze()
|
||||
assertFailsWith<InvalidMutabilityException> { a3[1]++ }
|
||||
|
||||
val a4 = LongArray(2)
|
||||
a4[1]++
|
||||
a4.freeze()
|
||||
assertFailsWith<InvalidMutabilityException> { a4[1]++ }
|
||||
|
||||
val a5 = BooleanArray(2)
|
||||
a5[1] = true
|
||||
a5.freeze()
|
||||
assertFailsWith<InvalidMutabilityException> { a5[1] = false }
|
||||
|
||||
val a6 = CharArray(2)
|
||||
a6[1] = 'a'
|
||||
a6.freeze()
|
||||
assertFailsWith<InvalidMutabilityException> { a6[1] = 'b' }
|
||||
|
||||
val a7 = FloatArray(2)
|
||||
a7[1] = 1.0f
|
||||
a7.freeze()
|
||||
assertFailsWith<InvalidMutabilityException> { a7[1] = 2.0f }
|
||||
|
||||
val a8 = DoubleArray(2)
|
||||
a8[1] = 1.0
|
||||
a8.freeze()
|
||||
assertFailsWith<InvalidMutabilityException> { a8[1] = 2.0 }
|
||||
|
||||
// Ensure that String and integral boxes are frozen by default, by passing local to the worker.
|
||||
val worker = Worker.start()
|
||||
var data: Any = "Hello" + " " + "world"
|
||||
assert(data.isFrozen)
|
||||
worker.execute(TransferMode.SAFE, { data } ) {
|
||||
input -> println("Worker 1: $input")
|
||||
}.result
|
||||
|
||||
data = 42
|
||||
assert(data.isFrozen)
|
||||
worker.execute(TransferMode.SAFE, { data } ) {
|
||||
input -> println("Worker2: $input")
|
||||
}.result
|
||||
|
||||
data = 239.0
|
||||
assert(data.isFrozen)
|
||||
worker.execute(TransferMode.SAFE, { data } ) {
|
||||
input -> println("Worker3: $input")
|
||||
}.result
|
||||
|
||||
data = 'a'
|
||||
assert(data.isFrozen)
|
||||
worker.execute(TransferMode.SAFE, { data } ) {
|
||||
input -> println("Worker4: $input")
|
||||
}.result
|
||||
|
||||
worker.requestTermination().result
|
||||
|
||||
println("OK")
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.freeze3
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
import kotlin.native.concurrent.*
|
||||
|
||||
object AnObject {
|
||||
var x = 1
|
||||
}
|
||||
|
||||
@ThreadLocal
|
||||
object Mutable {
|
||||
var x = 2
|
||||
}
|
||||
|
||||
val topLevelInline: ULong = 0xc3a5c85c97cb3127U
|
||||
|
||||
@Test fun runTest1() {
|
||||
assertEquals(1, AnObject.x)
|
||||
if (Platform.memoryModel == MemoryModel.STRICT) {
|
||||
assertFailsWith<InvalidMutabilityException> {
|
||||
AnObject.x++
|
||||
}
|
||||
assertEquals(1, AnObject.x)
|
||||
} else {
|
||||
AnObject.x++
|
||||
assertEquals(2, AnObject.x)
|
||||
}
|
||||
|
||||
Mutable.x++
|
||||
assertEquals(3, Mutable.x)
|
||||
println("OK")
|
||||
}
|
||||
|
||||
@Test fun runTest2() {
|
||||
val ok = AtomicInt(0)
|
||||
withWorker() {
|
||||
executeAfter(0, {
|
||||
assertEquals(0xc3a5c85c97cb3127U, topLevelInline)
|
||||
ok.increment()
|
||||
}.freeze())
|
||||
}
|
||||
assertEquals(1, ok.value)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.freeze4
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
import kotlin.native.concurrent.*
|
||||
|
||||
data class Data(val x: Int, val s: String, val next: Data? = null)
|
||||
|
||||
@Test fun runTest() {
|
||||
val data1 = Data(1, "")
|
||||
data1.freeze()
|
||||
assertFailsWith<FreezingException> {
|
||||
data1.ensureNeverFrozen()
|
||||
}
|
||||
|
||||
val dataNF = Data(42, "42")
|
||||
dataNF.ensureNeverFrozen()
|
||||
val data2 = Data(2, "2", dataNF)
|
||||
assertFailsWith<FreezingException> {
|
||||
data2.freeze()
|
||||
}
|
||||
assert(!data2.isFrozen)
|
||||
println("OK")
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.freeze5
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
object Keys {
|
||||
internal val myMap: Map<String, List<String>> = mapOf(
|
||||
"val1" to listOf("a1", "a2", "a3"),
|
||||
"val2" to listOf("b1", "b2")
|
||||
)
|
||||
|
||||
fun getKey(name: String): String {
|
||||
for (key in myMap.keys) {
|
||||
if (key == name) {
|
||||
return key
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
fun getValue(name: String): String {
|
||||
for (value in myMap.values) {
|
||||
if (value.contains(name)) {
|
||||
return name
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
fun getEntry(name: String): String {
|
||||
for (entry in myMap.entries) {
|
||||
if (entry.key == name) {
|
||||
return entry.key
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
}
|
||||
@Test fun runTest() {
|
||||
assertEquals("val2", Keys.getKey("val2"))
|
||||
assertEquals("a1", Keys.getValue("a1"))
|
||||
assertEquals("val1", Keys.getEntry("val1"))
|
||||
println("OK")
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.freeze6
|
||||
|
||||
import kotlin.test.*
|
||||
import kotlin.native.concurrent.*
|
||||
import kotlin.native.ref.*
|
||||
|
||||
data class Hi(val s: String)
|
||||
data class Nested(val hi: Hi)
|
||||
|
||||
@Test
|
||||
fun ensureNeverFrozenNoFreezeChild(){
|
||||
val noFreeze = Hi("qwert")
|
||||
noFreeze.ensureNeverFrozen()
|
||||
|
||||
val nested = Nested(noFreeze)
|
||||
assertFails { nested.freeze() }
|
||||
|
||||
println("OK")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun ensureNeverFrozenFailsTarget(){
|
||||
val noFreeze = Hi("qwert")
|
||||
noFreeze.ensureNeverFrozen()
|
||||
|
||||
assertFalse(noFreeze.isFrozen)
|
||||
assertFails { noFreeze.freeze() }
|
||||
assertFalse(noFreeze.isFrozen)
|
||||
println("OK")
|
||||
}
|
||||
|
||||
fun createRef1(): FreezableAtomicReference<Any?> {
|
||||
val ref = FreezableAtomicReference<Any?>(null)
|
||||
ref.value = ref
|
||||
ref.freeze()
|
||||
ref.value = null
|
||||
return ref
|
||||
}
|
||||
|
||||
var global = 0
|
||||
|
||||
@Test
|
||||
fun ensureFreezableHandlesCycles1() {
|
||||
val ref = createRef1()
|
||||
kotlin.native.internal.GC.collect()
|
||||
|
||||
val obj: Any = ref
|
||||
global = obj.hashCode()
|
||||
}
|
||||
|
||||
class Node(var ref: Any?)
|
||||
|
||||
/**
|
||||
* ref1 -> Node <- ref3
|
||||
* | /\
|
||||
* V |
|
||||
* ref2 ---
|
||||
*/
|
||||
fun createRef2(): Pair<FreezableAtomicReference<Node?>, Any> {
|
||||
val ref1 = FreezableAtomicReference<Node?>(null)
|
||||
|
||||
val node = Node(null)
|
||||
val ref3 = FreezableAtomicReference<Any?>(node)
|
||||
val ref2 = FreezableAtomicReference<Any?>(ref3)
|
||||
|
||||
node.ref = ref2
|
||||
ref1.value = node
|
||||
|
||||
ref1.freeze()
|
||||
ref3.value = null
|
||||
|
||||
assertTrue(node.isFrozen)
|
||||
assertTrue(ref1.isFrozen)
|
||||
assertTrue(ref2.isFrozen)
|
||||
assertTrue(ref3.isFrozen)
|
||||
|
||||
return ref1 to ref2
|
||||
}
|
||||
|
||||
@Test
|
||||
fun ensureFreezableHandlesCycles2() {
|
||||
val (ref, obj) = createRef2()
|
||||
kotlin.native.internal.GC.collect()
|
||||
|
||||
assertTrue(obj.toString().length > 0)
|
||||
global = ref.value!!.ref!!.hashCode()
|
||||
}
|
||||
|
||||
fun createRef3(): FreezableAtomicReference<Any?> {
|
||||
val ref = FreezableAtomicReference<Any?>(null)
|
||||
val node = Node(ref)
|
||||
ref.value = node
|
||||
ref.freeze()
|
||||
|
||||
assertTrue(node.isFrozen)
|
||||
assertTrue(ref.isFrozen)
|
||||
|
||||
return ref
|
||||
}
|
||||
|
||||
@Test
|
||||
fun ensureFreezableHandlesCycles3() {
|
||||
val ref = createRef3()
|
||||
ref.value = null
|
||||
kotlin.native.internal.GC.collect()
|
||||
|
||||
val obj: Any = ref
|
||||
assertTrue(obj.toString().length > 0)
|
||||
global = obj.hashCode()
|
||||
}
|
||||
|
||||
lateinit var weakRef: WeakReference<Any>
|
||||
|
||||
fun createRef4(): FreezableAtomicReference<Any?> {
|
||||
val ref = FreezableAtomicReference<Any?>(null)
|
||||
val node = Node(ref)
|
||||
weakRef = WeakReference(node)
|
||||
ref.value = node
|
||||
ref.freeze()
|
||||
assertTrue(weakRef.get() != null)
|
||||
return ref
|
||||
}
|
||||
|
||||
@Test
|
||||
fun ensureWeakRefNotLeaks1() {
|
||||
val ref = createRef4()
|
||||
ref.value = null
|
||||
// We cannot check weakRef.get() here, as value read will be stored in the stack slot,
|
||||
// and thus hold weak reference from release.
|
||||
kotlin.native.internal.GC.collect()
|
||||
|
||||
assertTrue(weakRef.get() == null)
|
||||
}
|
||||
|
||||
lateinit var node1: Node
|
||||
lateinit var weakNode2: WeakReference<Node>
|
||||
|
||||
fun createRef5() {
|
||||
val ref = FreezableAtomicReference<Any?>(null)
|
||||
node1 = Node(ref)
|
||||
val node2 = Node(node1)
|
||||
weakNode2 = WeakReference(node2)
|
||||
ref.value = node2
|
||||
node1.freeze()
|
||||
assertTrue(weakNode2.get() != null)
|
||||
ref.value = null
|
||||
}
|
||||
|
||||
@Test
|
||||
fun ensureWeakRefNotLeaks2() {
|
||||
createRef5()
|
||||
kotlin.native.internal.GC.collect()
|
||||
assertTrue(weakNode2.get() == null)
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.freeze_stress
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
import kotlin.native.concurrent.*
|
||||
|
||||
class Random(private var seed: Int) {
|
||||
fun next(): Int {
|
||||
seed = (1103515245 * seed + 12345) and 0x7fffffff
|
||||
return seed
|
||||
}
|
||||
|
||||
fun next(maxExclusiveValue: Int) = if (maxExclusiveValue == 0) 0 else next() % maxExclusiveValue
|
||||
|
||||
fun next(minInclusiveValue: Int, maxInclusiveValue: Int) =
|
||||
minInclusiveValue + next(maxInclusiveValue - minInclusiveValue + 1)
|
||||
}
|
||||
|
||||
class Node(val id: Int) {
|
||||
var numberOfEdges = 0
|
||||
|
||||
lateinit var edge0: Node
|
||||
lateinit var edge1: Node
|
||||
lateinit var edge2: Node
|
||||
lateinit var edge3: Node
|
||||
lateinit var edge4: Node
|
||||
lateinit var edge5: Node
|
||||
lateinit var edge6: Node
|
||||
lateinit var edge7: Node
|
||||
lateinit var edge8: Node
|
||||
lateinit var edge9: Node
|
||||
|
||||
fun addEdge(child: Node) {
|
||||
when (numberOfEdges) {
|
||||
0 -> edge0 = child
|
||||
1 -> edge1 = child
|
||||
2 -> edge2 = child
|
||||
3 -> edge3 = child
|
||||
4 -> edge4 = child
|
||||
5 -> edge5 = child
|
||||
6 -> edge6 = child
|
||||
7 -> edge7 = child
|
||||
8 -> edge8 = child
|
||||
9 -> edge9 = child
|
||||
else -> error("Too many edges")
|
||||
}
|
||||
++numberOfEdges
|
||||
}
|
||||
|
||||
fun getEdges(): List<Node> {
|
||||
val result = mutableListOf<Node>()
|
||||
if (numberOfEdges > 0) result += edge0
|
||||
if (numberOfEdges > 1) result += edge1
|
||||
if (numberOfEdges > 2) result += edge2
|
||||
if (numberOfEdges > 3) result += edge3
|
||||
if (numberOfEdges > 4) result += edge4
|
||||
if (numberOfEdges > 5) result += edge5
|
||||
if (numberOfEdges > 6) result += edge6
|
||||
if (numberOfEdges > 7) result += edge7
|
||||
if (numberOfEdges > 8) result += edge8
|
||||
if (numberOfEdges > 9) result += edge9
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
class Graph(val nodes: List<Node>, val roots: List<Node>)
|
||||
|
||||
fun min(x: Int, y: Int) = if (x < y) x else y
|
||||
fun max(x: Int, y: Int) = if (x > y) x else y
|
||||
|
||||
@ThreadLocal
|
||||
val random = Random(42)
|
||||
|
||||
fun generate(condensationSize: Int, branchingFactor: Int, swellingFactor: Int): Graph {
|
||||
var id = 0
|
||||
val nodes = mutableListOf<Node>()
|
||||
|
||||
fun genDAG(n: Int): Node {
|
||||
val node = Node(id++)
|
||||
nodes += node
|
||||
if (n == 1) return node
|
||||
val numberOfChildren = random.next(1, min(n - 1, branchingFactor))
|
||||
val used = BooleanArray(n)
|
||||
val points = IntArray(numberOfChildren + 1)
|
||||
points[0] = 0
|
||||
points[numberOfChildren] = n - 1
|
||||
used[0] = true
|
||||
used[n - 1] = true
|
||||
for (i in 1 until numberOfChildren) {
|
||||
var p: Int
|
||||
do {
|
||||
p = random.next(1, n - 1)
|
||||
} while (used[p])
|
||||
used[p] = true
|
||||
points[i] = p
|
||||
}
|
||||
points.sort()
|
||||
for (i in 1..numberOfChildren) {
|
||||
val childSize = points[i] - points[i - 1]
|
||||
val child = genDAG(childSize)
|
||||
if (random.next(2) == 0)
|
||||
node.addEdge(child)
|
||||
else
|
||||
child.addEdge(node)
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
genDAG(condensationSize)
|
||||
|
||||
val numberOfEnters = IntArray(condensationSize)
|
||||
for (node in nodes)
|
||||
for (edge in node.getEdges())
|
||||
++numberOfEnters[edge.id]
|
||||
val roots = nodes.filter { numberOfEnters[it.id] == 0 }
|
||||
for (i in 0 until condensationSize) {
|
||||
val node = nodes[i]
|
||||
val componentSize = random.next(1, swellingFactor)
|
||||
if (componentSize == 1 && random.next(2) == 0)
|
||||
continue
|
||||
val component = Array(componentSize) {
|
||||
if (it == 0) node else Node(id++).also { nodes += it }
|
||||
}
|
||||
for (j in 0 until componentSize)
|
||||
component[j].addEdge(component[(j - 1 + componentSize) % componentSize])
|
||||
val numberOfAdditionalEdges = random.next((componentSize + 1) / 2)
|
||||
for (j in 0 until numberOfAdditionalEdges)
|
||||
component[random.next(componentSize)].addEdge(component[random.next(componentSize)])
|
||||
}
|
||||
|
||||
return Graph(nodes, roots)
|
||||
}
|
||||
|
||||
fun freezeOneGraph() {
|
||||
val graph = generate(100, 5, 20)
|
||||
graph.roots.forEach { it.freeze() }
|
||||
for (node in graph.nodes)
|
||||
assert (node.isFrozen, { "All nodes should be frozen" })
|
||||
}
|
||||
|
||||
@Test fun runTest() {
|
||||
for (i in 0..1000) {
|
||||
freezeOneGraph()
|
||||
}
|
||||
println("OK")
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.lazy0
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
import kotlin.native.concurrent.*
|
||||
|
||||
data class Data(val x: Int, val y: String)
|
||||
|
||||
object Immutable {
|
||||
val x by atomicLazy {
|
||||
42
|
||||
}
|
||||
}
|
||||
|
||||
object Immutable2 {
|
||||
val y by atomicLazy {
|
||||
Data(239, "Kotlin")
|
||||
}
|
||||
}
|
||||
|
||||
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 ->
|
||||
workers[workerIndex].execute(TransferMode.SAFE, { "" }) { _ -> Immutable2.y }
|
||||
})
|
||||
futures.forEach { set += it.result }
|
||||
}
|
||||
assertEquals(set.size, 1)
|
||||
assertEquals(set.single(), Immutable2.y)
|
||||
}
|
||||
|
||||
fun testFrozenLazy(workers: Array<Worker>) {
|
||||
// To make sure it is always frozen, and we don't race in relaxed mode.
|
||||
Immutable3.freeze()
|
||||
val set = mutableSetOf<Int>()
|
||||
for (attempt in 1 .. 3) {
|
||||
val futures = Array(workers.size, { workerIndex ->
|
||||
workers[workerIndex].execute(TransferMode.SAFE, { "" }) { _ -> 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, { _ -> Worker.start()})
|
||||
testSingleData(workers)
|
||||
testFrozenLazy(workers)
|
||||
testLiquidLazy()
|
||||
|
||||
println("OK")
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.lazy1
|
||||
|
||||
import kotlin.test.*
|
||||
import kotlin.native.concurrent.*
|
||||
|
||||
class Lazy {
|
||||
val x = 17
|
||||
val self by lazy { this }
|
||||
val recursion: Int by lazy {
|
||||
if (x < 17) 42 else recursion
|
||||
}
|
||||
val freezer: Int by lazy {
|
||||
freeze()
|
||||
42
|
||||
}
|
||||
val thrower: String by lazy {
|
||||
if (x < 100) throw IllegalArgumentException()
|
||||
"FAIL"
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun runTest1() {
|
||||
assertFailsWith<IllegalStateException> {
|
||||
println(Lazy().recursion)
|
||||
}
|
||||
assertFailsWith<IllegalStateException> {
|
||||
println(Lazy().freeze().recursion)
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun runTest2() {
|
||||
var sum = 0
|
||||
for (i in 1 .. 100) {
|
||||
val self = Lazy().freeze()
|
||||
assertEquals(self, self.self)
|
||||
sum += self.self.hashCode()
|
||||
}
|
||||
println("OK")
|
||||
}
|
||||
|
||||
|
||||
@Test fun runTest3() {
|
||||
assertFailsWith<InvalidMutabilityException> {
|
||||
println(Lazy().freezer)
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun runTest4() {
|
||||
val self = Lazy()
|
||||
repeat(10) {
|
||||
assertFailsWith<IllegalArgumentException> {
|
||||
println(self.thrower)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.
|
||||
*/
|
||||
import kotlin.test.*
|
||||
|
||||
object Foo {
|
||||
val bar = Bar()
|
||||
}
|
||||
|
||||
class Bar {
|
||||
val f by lazy {
|
||||
foo()
|
||||
}
|
||||
|
||||
fun foo() = 123
|
||||
}
|
||||
|
||||
fun printAll() {
|
||||
println(Foo.bar.f)
|
||||
}
|
||||
|
||||
// This test is extracted from the real problem found in kotlinx.serialization, where zeroing out
|
||||
// initializer field in frozen lazy object led to the crash, induced by breaking frozen objects'
|
||||
// invariant (initializer end up in the same container as the lazy object itself, so it was destroyed
|
||||
// earlier than it should when reference counter was decremented).
|
||||
fun main(args: Array<String>) {
|
||||
printAll()
|
||||
kotlin.native.internal.GC.collect()
|
||||
println("OK")
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import kotlin.native.concurrent.*
|
||||
import kotlin.native.ref.*
|
||||
import kotlin.test.*
|
||||
|
||||
fun main() {
|
||||
test1()
|
||||
test2()
|
||||
}
|
||||
|
||||
fun test1() {
|
||||
ensureGetsCollectedFrozenAndNotFrozen { LazyCapturesThis() }
|
||||
ensureGetsCollectedFrozenAndNotFrozen {
|
||||
val l = LazyCapturesThis()
|
||||
l.bar
|
||||
l
|
||||
}
|
||||
ensureGetsCollected {
|
||||
val l = LazyCapturesThis().freeze()
|
||||
l.bar
|
||||
l
|
||||
}
|
||||
}
|
||||
|
||||
class LazyCapturesThis {
|
||||
fun foo() = 42
|
||||
val bar by lazy { foo() }
|
||||
}
|
||||
|
||||
fun test2() {
|
||||
ensureGetsCollectedFrozenAndNotFrozen { Throwable() }
|
||||
ensureGetsCollectedFrozenAndNotFrozen {
|
||||
val throwable = Throwable()
|
||||
throwable.getStackTrace()
|
||||
throwable
|
||||
}
|
||||
ensureGetsCollected {
|
||||
val throwable = Throwable().freeze()
|
||||
throwable.getStackTrace()
|
||||
throwable
|
||||
}
|
||||
}
|
||||
|
||||
fun ensureGetsCollectedFrozenAndNotFrozen(create: () -> Any) {
|
||||
ensureGetsCollected { create().freeze() }
|
||||
ensureGetsCollected(create)
|
||||
}
|
||||
|
||||
fun ensureGetsCollected(create: () -> Any) {
|
||||
val ref = makeWeakRef(create)
|
||||
kotlin.native.internal.GC.collect()
|
||||
assertNull(ref.get())
|
||||
}
|
||||
|
||||
fun makeWeakRef(create: () -> Any) = WeakReference(create())
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import kotlin.native.concurrent.*
|
||||
import kotlinx.cinterop.*
|
||||
|
||||
fun main() {
|
||||
val worker = Worker.start()
|
||||
// Make sure worker is initialized.
|
||||
worker.execute(TransferMode.SAFE, {}, {}).result;
|
||||
StableRef.create(Any())
|
||||
worker.requestTermination().result
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import kotlin.native.concurrent.*
|
||||
import kotlinx.cinterop.*
|
||||
|
||||
fun main() {
|
||||
val worker = Worker.start()
|
||||
// Make sure worker is initialized.
|
||||
worker.execute(TransferMode.SAFE, {}, {}).result;
|
||||
StableRef.create(Any())
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package runtime.workers.mutableData1
|
||||
|
||||
import kotlin.native.concurrent.*
|
||||
import kotlin.test.*
|
||||
|
||||
// See https://youtrack.jetbrains.com/issue/KT-39145
|
||||
@Test
|
||||
fun kt39145_1() = withWorker {
|
||||
execute(TransferMode.SAFE, { "test" }) {
|
||||
val data = MutableData(1_000)
|
||||
val bytes = byteArrayOf(0, 10, 20, 30)
|
||||
data.append(bytes, 0, 4)
|
||||
assertEquals(4, data.size)
|
||||
|
||||
assertContentsEquals(bytes, data)
|
||||
}.result
|
||||
}
|
||||
|
||||
@Test
|
||||
fun kt39145_2() = withWorker {
|
||||
val externalData = MutableData(1_000)
|
||||
execute(TransferMode.SAFE, { externalData }) {
|
||||
val bytes = byteArrayOf(0, 10, 20, 30)
|
||||
it.append(bytes, 0, 4)
|
||||
assertEquals(4, it.size)
|
||||
|
||||
assertContentsEquals(bytes, it)
|
||||
}.result
|
||||
}
|
||||
|
||||
@Test
|
||||
fun kt39145_3() {
|
||||
val mainThreadData = MutableData(1_000)
|
||||
val bytes = byteArrayOf(0, 10, 20, 30)
|
||||
mainThreadData.append(bytes, 0, 4)
|
||||
assertEquals(4, mainThreadData.size)
|
||||
|
||||
assertContentsEquals(bytes, mainThreadData)
|
||||
}
|
||||
|
||||
private fun assertContentsEquals(expected: ByteArray, actual: MutableData) {
|
||||
assertEquals(expected.size, actual.size)
|
||||
|
||||
expected.forEachIndexed { index, byte ->
|
||||
assertEquals(byte, actual.get(index))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.worker0
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
import kotlin.native.concurrent.*
|
||||
|
||||
@Test fun runTest() {
|
||||
val worker = Worker.start()
|
||||
val future = worker.execute(TransferMode.SAFE, { "Input" }) {
|
||||
input ->
|
||||
assertEquals(1, 1)
|
||||
assertFailsWith<AssertionError> { assertEquals(1, 2) }
|
||||
input + " processed"
|
||||
}
|
||||
future.consume {
|
||||
result -> println("Got $result")
|
||||
}
|
||||
worker.requestTermination().result
|
||||
println("OK")
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.worker1
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
import kotlin.native.concurrent.*
|
||||
|
||||
@Test fun runTest() {
|
||||
val COUNT = 5
|
||||
val workers = Array(COUNT, { _ -> Worker.start()})
|
||||
|
||||
for (attempt in 1 .. 3) {
|
||||
val futures = Array(workers.size,
|
||||
{ i -> workers[i].execute(TransferMode.SAFE, { "$attempt: Input $i" })
|
||||
{ input -> input + " processed" }
|
||||
})
|
||||
futures.forEachIndexed { index, future ->
|
||||
future.consume {
|
||||
result ->
|
||||
if ("$attempt: Input $index processed" != result) {
|
||||
println("Got unexpected $result")
|
||||
throw Error(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
workers.forEach {
|
||||
it.requestTermination().result
|
||||
}
|
||||
println("OK")
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
package runtime.workers.worker10
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
import kotlin.native.concurrent.*
|
||||
import kotlin.native.ref.WeakReference
|
||||
import kotlinx.cinterop.StableRef
|
||||
|
||||
class Data(val x: Int)
|
||||
|
||||
val topInt = 1
|
||||
val topString = "string"
|
||||
var topStringVar = "string"
|
||||
val topSharedStringWithGetter: String
|
||||
get() = "top"
|
||||
val topData = Data(42)
|
||||
@SharedImmutable
|
||||
val topSharedData = Data(43)
|
||||
|
||||
@Test fun runTest1() {
|
||||
val worker = Worker.start()
|
||||
|
||||
assertEquals(1, topInt)
|
||||
assertEquals("string", topString)
|
||||
assertEquals(42, topData.x)
|
||||
assertEquals(43, topSharedData.x)
|
||||
assertEquals("top", topSharedStringWithGetter)
|
||||
|
||||
worker.execute(TransferMode.SAFE, { -> }, {
|
||||
it -> topInt == 1
|
||||
}).consume {
|
||||
result -> assertEquals(true, result)
|
||||
}
|
||||
|
||||
worker.execute(TransferMode.SAFE, { -> }, {
|
||||
it -> topString == "string"
|
||||
}).consume {
|
||||
result -> assertEquals(true, result)
|
||||
}
|
||||
|
||||
worker.execute(TransferMode.SAFE, { -> }, {
|
||||
it -> try {
|
||||
topStringVar == "string"
|
||||
} catch (e: IncorrectDereferenceException) {
|
||||
false
|
||||
}
|
||||
}).consume {
|
||||
result -> assertEquals(Platform.memoryModel == MemoryModel.RELAXED, result)
|
||||
}
|
||||
|
||||
worker.execute(TransferMode.SAFE, { -> }, {
|
||||
it -> try {
|
||||
topSharedStringWithGetter == "top"
|
||||
} catch (e: IncorrectDereferenceException) {
|
||||
false
|
||||
}
|
||||
}).consume {
|
||||
result -> assertEquals(true, result)
|
||||
}
|
||||
|
||||
worker.execute(TransferMode.SAFE, { -> }, {
|
||||
it -> try {
|
||||
topData.x == 42
|
||||
} catch (e: IncorrectDereferenceException) {
|
||||
false
|
||||
}
|
||||
}).consume {
|
||||
result -> assertEquals(Platform.memoryModel == MemoryModel.RELAXED, result)
|
||||
}
|
||||
|
||||
worker.execute(TransferMode.SAFE, { -> }, {
|
||||
it -> try {
|
||||
topSharedData.x == 43
|
||||
} catch (e: Throwable) {
|
||||
false
|
||||
}
|
||||
}).consume {
|
||||
result -> assertEquals(true, result)
|
||||
}
|
||||
|
||||
worker.requestTermination().result
|
||||
println("OK")
|
||||
}
|
||||
|
||||
val atomicRef = AtomicReference<Any?>(Any().freeze())
|
||||
@SharedImmutable
|
||||
val stableRef = StableRef.create(Any().freeze())
|
||||
val semaphore = AtomicInt(0)
|
||||
|
||||
@Test fun runTest2() {
|
||||
semaphore.value = 0
|
||||
val worker = Worker.start()
|
||||
val future = worker.execute(TransferMode.SAFE, { null }) {
|
||||
val value = atomicRef.value
|
||||
semaphore.increment()
|
||||
while (semaphore.value != 2) {}
|
||||
println(value.toString() != "")
|
||||
}
|
||||
while (semaphore.value != 1) {}
|
||||
atomicRef.value = null
|
||||
kotlin.native.internal.GC.collect()
|
||||
semaphore.increment()
|
||||
future.result
|
||||
worker.requestTermination().result
|
||||
}
|
||||
|
||||
@Test fun runTest3() {
|
||||
semaphore.value = 0
|
||||
val worker = Worker.start()
|
||||
val future = worker.execute(TransferMode.SAFE, { null }) {
|
||||
val value = stableRef.get()
|
||||
semaphore.increment()
|
||||
while (semaphore.value != 2) {}
|
||||
println(value.toString() != "")
|
||||
}
|
||||
while (semaphore.value != 1) {}
|
||||
stableRef.dispose()
|
||||
kotlin.native.internal.GC.collect()
|
||||
semaphore.increment()
|
||||
future.result
|
||||
worker.requestTermination().result
|
||||
}
|
||||
|
||||
fun <T: Any> ensureWeakIs(weak: WeakReference<T>, expected: T?) {
|
||||
assertEquals(expected, weak.get())
|
||||
}
|
||||
|
||||
val stableHolder1 = StableRef.create(("hello" to "world").freeze())
|
||||
|
||||
@Test fun runTest4() {
|
||||
val worker = Worker.start()
|
||||
semaphore.value = 0
|
||||
val future = worker.execute(TransferMode.SAFE, { WeakReference(stableHolder1.get()) }) {
|
||||
ensureWeakIs(it, "hello" to "world")
|
||||
semaphore.increment()
|
||||
while (semaphore.value != 2) {}
|
||||
kotlin.native.internal.GC.collect()
|
||||
ensureWeakIs(it, null)
|
||||
}
|
||||
while (semaphore.value != 1) {}
|
||||
stableHolder1.dispose()
|
||||
kotlin.native.internal.GC.collect()
|
||||
semaphore.increment()
|
||||
future.result
|
||||
worker.requestTermination().result
|
||||
}
|
||||
|
||||
val stableHolder2 = StableRef.create(("hello" to "world").freeze())
|
||||
|
||||
@Test fun runTest5() {
|
||||
val worker = Worker.start()
|
||||
semaphore.value = 0
|
||||
val future = worker.execute(TransferMode.SAFE, { WeakReference(stableHolder2.get()) }) {
|
||||
val value = it.get()
|
||||
semaphore.increment()
|
||||
while (semaphore.value != 2) {}
|
||||
kotlin.native.internal.GC.collect()
|
||||
assertEquals("hello" to "world", value)
|
||||
}
|
||||
while (semaphore.value != 1) {}
|
||||
stableHolder2.dispose()
|
||||
kotlin.native.internal.GC.collect()
|
||||
semaphore.increment()
|
||||
future.result
|
||||
worker.requestTermination().result
|
||||
}
|
||||
|
||||
val atomicRef2 = AtomicReference<Any?>(Any().freeze())
|
||||
@Test fun runTest6() {
|
||||
semaphore.value = 0
|
||||
val worker = Worker.start()
|
||||
val future = worker.execute(TransferMode.SAFE, { null }) {
|
||||
val value = atomicRef2.compareAndSwap(null, null)
|
||||
semaphore.increment()
|
||||
while (semaphore.value != 2) {}
|
||||
assertEquals(true, value.toString() != "")
|
||||
}
|
||||
while (semaphore.value != 1) {}
|
||||
atomicRef2.value = null
|
||||
kotlin.native.internal.GC.collect()
|
||||
semaphore.increment()
|
||||
future.result
|
||||
worker.requestTermination().result
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.worker11
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
import kotlin.native.concurrent.*
|
||||
import kotlinx.cinterop.convert
|
||||
|
||||
data class Job(val index: Int, var input: Int, var counter: Int)
|
||||
|
||||
fun initJobs(count: Int) = Array<Job?>(count) { i -> Job(i, i * 2, i)}
|
||||
|
||||
@Test fun runTest0() {
|
||||
val workers = Array(100, { _ -> Worker.start() })
|
||||
val jobs = initJobs(workers.size)
|
||||
val futures = Array(workers.size, { workerIndex ->
|
||||
workers[workerIndex].execute(TransferMode.SAFE, {
|
||||
val job = jobs[workerIndex]
|
||||
jobs[workerIndex] = null
|
||||
job!!
|
||||
}) { job ->
|
||||
job.counter += job.input
|
||||
job
|
||||
}
|
||||
})
|
||||
val futureSet = futures.toSet()
|
||||
var consumed = 0
|
||||
while (consumed < futureSet.size) {
|
||||
val ready = waitForMultipleFutures(futureSet, 10000)
|
||||
ready.forEach {
|
||||
it.consume { job ->
|
||||
assertEquals(job.index * 3, job.counter)
|
||||
jobs[job.index] = job
|
||||
}
|
||||
consumed++
|
||||
}
|
||||
}
|
||||
assertEquals(consumed, workers.size)
|
||||
workers.forEach {
|
||||
it.requestTermination().result
|
||||
}
|
||||
println("OK")
|
||||
}
|
||||
|
||||
val COUNT = 2
|
||||
|
||||
@SharedImmutable
|
||||
val counters = Array(COUNT) { AtomicInt(0) }
|
||||
|
||||
@Test fun runTest1() {
|
||||
val workers = Array(COUNT) { Worker.start() }
|
||||
// Ensure processQueue() can only be called on current Worker.
|
||||
workers.forEach {
|
||||
assertFailsWith<IllegalStateException> {
|
||||
it.processQueue()
|
||||
}
|
||||
}
|
||||
val futures = Array(workers.size) { workerIndex ->
|
||||
workers[workerIndex].execute(TransferMode.SAFE, {
|
||||
workerIndex
|
||||
}) {
|
||||
index ->
|
||||
assertEquals(0, counters[index].value)
|
||||
// Process following request.
|
||||
while (!Worker.current!!.processQueue()) {}
|
||||
// Ensure it has an effect.
|
||||
assertEquals(1, counters[index].value)
|
||||
// No more non-terminating tasks in this worker queue.
|
||||
assertEquals(false, Worker.current!!.processQueue())
|
||||
}
|
||||
}
|
||||
val futures2 = Array(workers.size) { workerIndex ->
|
||||
workers[workerIndex].execute(TransferMode.SAFE, {
|
||||
workerIndex
|
||||
}) { index ->
|
||||
assertEquals(0, counters[index].value)
|
||||
counters[index].increment()
|
||||
}
|
||||
}
|
||||
futures2.forEach { it.result }
|
||||
futures.forEach { it.result }
|
||||
workers.forEach {
|
||||
it.requestTermination().result
|
||||
}
|
||||
|
||||
// Ensure terminated workers are no longer there.
|
||||
workers.forEach {
|
||||
assertFailsWith<IllegalStateException> { it.execute(TransferMode.SAFE, { Unit }) { println("ERROR") } }
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun runTest2() {
|
||||
val workers = Array(COUNT) { Worker.start() }
|
||||
val futures = Array(workers.size) { workerIndex ->
|
||||
workers[workerIndex].execute(TransferMode.SAFE, { null }) {
|
||||
// Here we processed termination request.
|
||||
assertEquals(false, Worker.current.processQueue())
|
||||
}
|
||||
}
|
||||
workers.forEach {
|
||||
it.executeAfter(1000*1000*1000, {
|
||||
println("DELAY EXECUTED")
|
||||
assert(false)
|
||||
}.freeze())
|
||||
}
|
||||
workers.forEach {
|
||||
it.requestTermination(processScheduledJobs = false).result
|
||||
}
|
||||
// Process futures, ignoring possible cancelled ones.
|
||||
futures.forEach {
|
||||
try { it.result } catch (e: IllegalStateException) {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.worker2
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
import kotlin.native.concurrent.*
|
||||
|
||||
data class WorkerArgument(val intParam: Int, val stringParam: String)
|
||||
data class WorkerResult(val intResult: Int, val stringResult: String)
|
||||
|
||||
@Test fun runTest() {
|
||||
val COUNT = 5
|
||||
val workers = Array(COUNT, { _ -> Worker.start()})
|
||||
|
||||
for (attempt in 1 .. 3) {
|
||||
val futures = Array(workers.size, { workerIndex -> workers[workerIndex].execute(TransferMode.SAFE, {
|
||||
WorkerArgument(workerIndex, "attempt $attempt") }) { input ->
|
||||
var sum = 0
|
||||
for (i in 0..input.intParam * 1000) {
|
||||
sum += i
|
||||
}
|
||||
WorkerResult(sum, input.stringParam + " result")
|
||||
}
|
||||
})
|
||||
val futureSet = futures.toSet()
|
||||
var consumed = 0
|
||||
while (consumed < futureSet.size) {
|
||||
val ready = waitForMultipleFutures(futureSet, 10000)
|
||||
ready.forEach {
|
||||
it.consume { result ->
|
||||
if (result.stringResult != "attempt $attempt result") throw Error("Unexpected $result")
|
||||
consumed++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
workers.forEach {
|
||||
it.requestTermination().result
|
||||
}
|
||||
println("OK")
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.worker3
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
import kotlin.native.concurrent.*
|
||||
|
||||
data class DataParam(var int: Int)
|
||||
data class WorkerArgument(val intParam: Int, val dataParam: DataParam)
|
||||
data class WorkerResult(val intResult: Int, val stringResult: String)
|
||||
|
||||
@Test fun runTest() {
|
||||
main(emptyArray())
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val worker = Worker.start()
|
||||
val dataParam = DataParam(17)
|
||||
val future = try {
|
||||
worker.execute(TransferMode.SAFE,
|
||||
{ WorkerArgument(42, dataParam) }) {
|
||||
input -> WorkerResult(input.intParam, input.dataParam.toString() + " result")
|
||||
}
|
||||
} catch (e: IllegalStateException) {
|
||||
null
|
||||
}
|
||||
if (future != null && Platform.memoryModel == MemoryModel.STRICT)
|
||||
println("Fail 1")
|
||||
if (dataParam.int != 17) println("Fail 2")
|
||||
worker.requestTermination().result
|
||||
println("OK")
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.worker4
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
import kotlin.native.concurrent.*
|
||||
|
||||
@Test fun runTest1() {
|
||||
withWorker {
|
||||
val future = execute(TransferMode.SAFE, { 41 }) { input ->
|
||||
input + 1
|
||||
}
|
||||
future.consume { result ->
|
||||
println("Got $result")
|
||||
}
|
||||
}
|
||||
println("OK")
|
||||
}
|
||||
|
||||
@Test fun runTest2() {
|
||||
withWorker {
|
||||
val counter = AtomicInt(0)
|
||||
|
||||
executeAfter(0, {
|
||||
assertTrue(Worker.current.park(10_000_000, false))
|
||||
assertEquals(counter.value, 0)
|
||||
assertTrue(Worker.current.processQueue())
|
||||
assertEquals(1, counter.value)
|
||||
// Let main proceed.
|
||||
counter.increment() // counter becomes 2 here.
|
||||
assertTrue(Worker.current.park(10_000_000, true))
|
||||
assertEquals(3, counter.value)
|
||||
}.freeze())
|
||||
|
||||
executeAfter(0, {
|
||||
counter.increment()
|
||||
}.freeze())
|
||||
|
||||
while (counter.value < 2) {
|
||||
Worker.current.park(1_000)
|
||||
}
|
||||
|
||||
executeAfter(0, {
|
||||
counter.increment()
|
||||
}.freeze())
|
||||
|
||||
while (counter.value == 2) {
|
||||
Worker.current.park(1_000)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun runTest3() {
|
||||
val worker = Worker.start(name = "Lumberjack")
|
||||
val counter = AtomicInt(0)
|
||||
worker.executeAfter(0, {
|
||||
assertEquals("Lumberjack", Worker.current.name)
|
||||
counter.increment()
|
||||
}.freeze())
|
||||
|
||||
while (counter.value == 0) {
|
||||
Worker.current.park(1_000)
|
||||
}
|
||||
assertEquals("Lumberjack", worker.name)
|
||||
worker.requestTermination().result
|
||||
assertFailsWith<IllegalStateException> {
|
||||
println(worker.name)
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun runTest4() {
|
||||
val counter = AtomicInt(0)
|
||||
Worker.current.executeAfter(10_000, {
|
||||
counter.increment()
|
||||
}.freeze())
|
||||
assertTrue(Worker.current.park(1_000_000, process = true))
|
||||
assertEquals(1, counter.value)
|
||||
}
|
||||
|
||||
@Test fun runTest5() {
|
||||
val main = Worker.current
|
||||
val counter = AtomicInt(0)
|
||||
withWorker {
|
||||
executeAfter(1000, {
|
||||
main.executeAfter(1, {
|
||||
counter.increment()
|
||||
}.freeze())
|
||||
}.freeze())
|
||||
assertTrue(main.park(1000L * 1000 * 1000, process = true))
|
||||
assertEquals(1, counter.value)
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun runTest6() {
|
||||
// Ensure zero timeout works properly.
|
||||
Worker.current.park(0, process = true)
|
||||
}
|
||||
|
||||
@Test fun runTest7() {
|
||||
val counter = AtomicInt(0)
|
||||
withWorker {
|
||||
val f1 = execute(TransferMode.SAFE, { counter }) { counter ->
|
||||
Worker.current.park(Long.MAX_VALUE / 1000L, process = true)
|
||||
counter.increment()
|
||||
}
|
||||
// wait a bit
|
||||
Worker.current.park(10_000L)
|
||||
// submit a task
|
||||
val f2 = execute(TransferMode.SAFE, { counter }) { counter ->
|
||||
counter.increment()
|
||||
}
|
||||
f1.consume {}
|
||||
f2.consume {}
|
||||
assertEquals(2, counter.value)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.
|
||||
*/
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
import kotlin.native.concurrent.*
|
||||
|
||||
@Test fun runTest0() {
|
||||
val worker = Worker.start()
|
||||
val future = worker.execute(TransferMode.SAFE, { "zzz" }) {
|
||||
input -> input.length
|
||||
}
|
||||
future.consume {
|
||||
result -> println("Got $result")
|
||||
}
|
||||
worker.requestTermination().result
|
||||
println("OK")
|
||||
}
|
||||
|
||||
var done = false
|
||||
|
||||
@Test fun runTest1() {
|
||||
val worker = Worker.current
|
||||
done = false
|
||||
// Here we request execution of the operation on the current worker.
|
||||
worker.executeAfter(0, {
|
||||
done = true
|
||||
}.freeze())
|
||||
while (!done)
|
||||
worker.processQueue()
|
||||
}
|
||||
|
||||
// Ensure that termination of current worker on main thread doesn't lead to problems.
|
||||
@Test fun runTest2() {
|
||||
val worker = Worker.current
|
||||
val future = worker.requestTermination(false)
|
||||
worker.processQueue()
|
||||
assertEquals(future.state, FutureState.COMPUTED)
|
||||
future.consume {}
|
||||
// After termination request this worker is no longer addressable.
|
||||
assertFailsWith<IllegalStateException> { worker.executeAfter(0, {
|
||||
println("BUG!")
|
||||
}.freeze()) }
|
||||
}
|
||||
|
||||
fun main() {
|
||||
runTest0()
|
||||
runTest1()
|
||||
runTest2()
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.worker6
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
import kotlin.native.concurrent.*
|
||||
|
||||
@Test fun runTest1() {
|
||||
withWorker {
|
||||
val future = execute(TransferMode.SAFE, { 42 }) { input ->
|
||||
input.toString()
|
||||
}
|
||||
future.consume { result ->
|
||||
println("Got $result")
|
||||
}
|
||||
}
|
||||
println("OK")
|
||||
}
|
||||
|
||||
var int1 = 1
|
||||
val int2 = 77
|
||||
|
||||
@Test fun runTest2() {
|
||||
int1++
|
||||
withWorker {
|
||||
executeAfter(0, {
|
||||
assertFailsWith<IncorrectDereferenceException> {
|
||||
int1++
|
||||
}
|
||||
assertEquals(2, int1)
|
||||
assertEquals(77, int2)
|
||||
}.freeze())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.worker7
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
import kotlin.native.concurrent.*
|
||||
|
||||
@Test fun runTest() {
|
||||
val worker = Worker.start(false)
|
||||
val future = worker.execute(TransferMode.SAFE, { "Input" }) {
|
||||
input -> println(input)
|
||||
}
|
||||
future.consume {
|
||||
result -> println("Got $result")
|
||||
}
|
||||
|
||||
assertFailsWith<IllegalStateException> {
|
||||
println(worker.execute(TransferMode.SAFE, { null }, { _ -> throw Error("An error") }).result)
|
||||
}
|
||||
|
||||
worker.requestTermination().result
|
||||
|
||||
println("OK")
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.worker8
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
import kotlin.native.concurrent.*
|
||||
|
||||
data class SharedDataMember(val double: Double)
|
||||
|
||||
data class SharedData(val string: String, val int: Int, val member: SharedDataMember)
|
||||
|
||||
@Test fun runTest() {
|
||||
val worker = Worker.start()
|
||||
// Here we do rather strange thing. To test object detach API we detach object graph,
|
||||
// pass detached graph to a worker, where we manually reattached passed value.
|
||||
val future = worker.execute(TransferMode.SAFE, {
|
||||
DetachedObjectGraph { SharedData("Hello", 10, SharedDataMember(0.1)) }.asCPointer()
|
||||
}) {
|
||||
inputDetached ->
|
||||
val input = DetachedObjectGraph<SharedData>(inputDetached).attach()
|
||||
println(input)
|
||||
}
|
||||
future.consume {
|
||||
result -> println("Got $result")
|
||||
}
|
||||
worker.requestTermination().result
|
||||
println("OK")
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.worker9
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
import kotlin.native.concurrent.*
|
||||
|
||||
@Test fun runTest1() {
|
||||
withLock { println("zzz") }
|
||||
val worker = Worker.start()
|
||||
val future = worker.execute(TransferMode.SAFE, {}) {
|
||||
withLock {
|
||||
println("42")
|
||||
}
|
||||
}
|
||||
future.result
|
||||
worker.requestTermination().result
|
||||
println("OK")
|
||||
}
|
||||
|
||||
fun withLock(op: () -> Unit) {
|
||||
op()
|
||||
}
|
||||
|
||||
@Test fun runTest2() {
|
||||
val worker = Worker.start()
|
||||
val future = worker.execute(TransferMode.SAFE, {}) {
|
||||
val me = Worker.current
|
||||
var x = 1
|
||||
me.executeAfter (20000) {
|
||||
println("second ${++x}")
|
||||
}
|
||||
me.executeAfter(10000) {
|
||||
println("first ${++x}")
|
||||
}
|
||||
}
|
||||
worker.requestTermination().result
|
||||
}
|
||||
|
||||
@Test fun runTest3() {
|
||||
val worker = Worker.start()
|
||||
assertFailsWith<IllegalStateException> {
|
||||
worker.executeAfter {
|
||||
println("shall not happen")
|
||||
}
|
||||
}
|
||||
assertFailsWith<IllegalArgumentException> {
|
||||
worker.executeAfter(-1, {
|
||||
println("shall not happen")
|
||||
}.freeze())
|
||||
}
|
||||
|
||||
worker.executeAfter(0, {
|
||||
println("frozen OK")
|
||||
}.freeze())
|
||||
|
||||
worker.requestTermination().result
|
||||
}
|
||||
|
||||
class Node(var node: Node?, var outher: Node?)
|
||||
|
||||
fun makeCyclic(): Node {
|
||||
val inner = Node(null, null)
|
||||
inner.node = inner
|
||||
val outer = Node(null, null)
|
||||
inner.outher = outer
|
||||
return outer
|
||||
}
|
||||
|
||||
@Test fun runTest4() {
|
||||
val worker = Worker.start()
|
||||
|
||||
val future = worker.execute(TransferMode.SAFE, { }) {
|
||||
makeCyclic().also {
|
||||
kotlin.native.internal.GC.collect()
|
||||
}
|
||||
}
|
||||
assert(future.result != null)
|
||||
worker.requestTermination().result
|
||||
}
|
||||
Reference in New Issue
Block a user