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

Commit

Permalink
Add atomic references.
Browse files Browse the repository at this point in the history
  • Loading branch information
olonho committed Jun 17, 2018
1 parent e61deac commit b905026
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 5 deletions.
2 changes: 1 addition & 1 deletion backend.native/tests/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}

Expand Down
50 changes: 46 additions & 4 deletions backend.native/tests/runtime/workers/atomic0.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,57 @@ fun test1(workers: Array<Worker>) {

fun test2(workers: Array<Worker>) {
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<Worker>) {
val common = AtomicReference<Data>()
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<Data>()
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<InvalidMutabilityException> {
AtomicReference(Data(1))
}
assertFailsWith<InvalidMutabilityException> {
AtomicReference<Data>().compareAndSwap(null, Data(2))
}
}

@Test fun runTest() {
Expand All @@ -38,4 +78,6 @@ fun test2(workers: Array<Worker>) {

test1(workers)
test2(workers)
test3(workers)
test4()
}
14 changes: 14 additions & 0 deletions runtime/src/main/cpp/Atomic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/

#include "Atomic.h"
#include "Common.h"
#include "Types.h"

namespace {
Expand All @@ -33,6 +34,8 @@ template <typename T> 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);
}
Expand All @@ -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"
28 changes: 28 additions & 0 deletions runtime/src/main/kotlin/konan/worker/Atomics.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>(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
}

0 comments on commit b905026

Please sign in to comment.