From adfed22d709ad589c65f4f27e9dd285f46a0820f Mon Sep 17 00:00:00 2001 From: Nikolay Igotti Date: Fri, 15 Jun 2018 19:18:38 +0300 Subject: [PATCH] Add atomic references. --- backend.native/tests/build.gradle | 2 +- .../tests/runtime/workers/atomic0.kt | 50 +++++++++++++++++-- runtime/src/main/cpp/Atomic.cpp | 14 ++++++ .../src/main/kotlin/konan/worker/Atomics.kt | 28 +++++++++++ 4 files changed, 89 insertions(+), 5 deletions(-) diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index 0cdd57a45aa..5a67f241814 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -673,7 +673,7 @@ task freeze2(type: RunKonanTest) { task atomic0(type: RunKonanTest) { disabled = (project.testTarget == 'wasm32') // Workers need pthreads. - goldValue = "35\n" + "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n" + goldValue = "35\n" + "20\n" source = "runtime/workers/atomic0.kt" } diff --git a/backend.native/tests/runtime/workers/atomic0.kt b/backend.native/tests/runtime/workers/atomic0.kt index f7a241a091c..685d009fc7e 100644 --- a/backend.native/tests/runtime/workers/atomic0.kt +++ b/backend.native/tests/runtime/workers/atomic0.kt @@ -19,17 +19,57 @@ fun test1(workers: Array) { fun test2(workers: Array) { val atomic = AtomicInt(0) + val counter = AtomicInt(0) val futures = Array(workers.size, { workerIndex -> - workers[workerIndex].schedule(TransferMode.CHECKED, { atomic to workerIndex }) { - (place, index) -> + workers[workerIndex].schedule(TransferMode.CHECKED, { Triple(atomic, workerIndex, counter) }) { + (place, index, result) -> while (place.compareAndSwap(index, index + 1) != index) {} - println(index) + result.increment() == index + 1 + } + }) + futures.forEach { + assertEquals(it.result(), true) + } + println(counter.get()) +} + +data class Data(val value: Int) + +fun test3(workers: Array) { + val common = AtomicReference() + val futures = Array(workers.size, { workerIndex -> + workers[workerIndex].schedule(TransferMode.CHECKED, { Pair(common, workerIndex) }) { + (place, index) -> + val mine = Data(index).freeze() + // Try to publish our own data, until successful, in a tight loop. + while (place.compareAndSwap(null, mine) != null) {} } }) + val seen = mutableSetOf() + for (i in 0 until workers.size) { + do { + val current = common.get() + if (current != null && !seen.contains(current)) { + seen += current + // Let others publish. + assertEquals(common.compareAndSwap(current, null), current) + break + } + } while (true) + } futures.forEach { it.result() } - println(atomic.get()) + assertEquals(seen.size, workers.size) +} + +fun test4() { + assertFailsWith { + AtomicReference(Data(1)) + } + assertFailsWith { + AtomicReference().compareAndSwap(null, Data(2)) + } } @Test fun runTest() { @@ -38,4 +78,6 @@ fun test2(workers: Array) { test1(workers) test2(workers) + test3(workers) + test4() } \ No newline at end of file diff --git a/runtime/src/main/cpp/Atomic.cpp b/runtime/src/main/cpp/Atomic.cpp index 481ee3dbeb7..0fd8941a3a0 100644 --- a/runtime/src/main/cpp/Atomic.cpp +++ b/runtime/src/main/cpp/Atomic.cpp @@ -15,6 +15,7 @@ */ #include "Atomic.h" +#include "Common.h" #include "Types.h" namespace { @@ -33,6 +34,8 @@ template T compareAndSwapImpl(KRef thiz, T expectedValue, T newValu extern "C" { +RUNTIME_NORETURN void ThrowInvalidMutabilityException(); + KInt Kotlin_AtomicInt_addAndGet(KRef thiz, KInt delta) { return addAndGetImpl(thiz, delta); } @@ -53,4 +56,15 @@ KNativePtr Kotlin_AtomicNativePtr_compareAndSwap(KRef thiz, KNativePtr expectedV return compareAndSwapImpl(thiz, expectedValue, newValue); } +void Kotlin_AtomicReference_checkIfFrozen(KRef value) { + if (value != nullptr && !value->container()->permanentOrFrozen()) { + ThrowInvalidMutabilityException(); + } +} + +KRef Kotlin_AtomicReference_compareAndSwap(KRef thiz, KRef expectedValue, KRef newValue) { + Kotlin_AtomicReference_checkIfFrozen(newValue); + return compareAndSwapImpl(thiz, expectedValue, newValue); +} + } // extern "C" \ No newline at end of file diff --git a/runtime/src/main/kotlin/konan/worker/Atomics.kt b/runtime/src/main/kotlin/konan/worker/Atomics.kt index 12ab28f4e97..081b432ed12 100644 --- a/runtime/src/main/kotlin/konan/worker/Atomics.kt +++ b/runtime/src/main/kotlin/konan/worker/Atomics.kt @@ -178,4 +178,32 @@ class AtomicNativePtr(private var value: NativePtr) { * Returns the current value. */ fun get(): NativePtr = value +} + +@SymbolName("Kotlin_AtomicReference_checkIfFrozen") +external private fun checkIfFrozen(ref: Any?) + +@Frozen +class AtomicReference(private var value: T? = null) { + /** + * Creates a new atomic reference pointing to given [ref]. If reference is not frozen, + * @InvalidMutabilityException is thrown. + */ + init { + checkIfFrozen(value) + } + + /** + * Compares value with [expected] and replaces it with [new] value if values matches. + * If [new] value is not null, it must be frozen or permanent object, otherwise an + * @InvalidMutabilityException is thrown. + * Returns the old value. + */ + @SymbolName("Kotlin_AtomicReference_compareAndSwap") + external fun compareAndSwap(expected: T?, new: T?): T? + + /** + * Returns the current value. + */ + public fun get(): T? = value } \ No newline at end of file