kotlinx.atomicfu compiler plugin for JS_IR backend (#4581)

* kotlinx.atomicfu compiler plugin for JS_IR

Support transformations of atomic operations introduced by the kotlinx.atomicfu library for the JS_IR backend. Compiler plugin is applied externally by the kotlinx.atomicfu gradle plugin.

* Apply compiler plugin for JS platform only

* New plugin test structure

* testGroupOutputDirPrefix changed
This commit is contained in:
mvicsokolova
2021-12-01 22:33:13 +03:00
committed by GitHub
parent 05695761ec
commit 93561a1a55
45 changed files with 2655 additions and 4 deletions
@@ -0,0 +1,131 @@
import kotlinx.atomicfu.*
import kotlin.test.*
class IntArithmetic {
val _x = atomic(0)
val x get() = _x.value
}
class LongArithmetic {
val _x = atomic(4294967296)
val x get() = _x.value
val y = atomic(5000000000)
val z = atomic(2424920024888888848)
val max = atomic(9223372036854775807)
}
class BooleanArithmetic {
val _x = atomic(false)
val x get() = _x.value
}
class ReferenceArithmetic {
val _x = atomic<String?>(null)
}
class ArithmeticTest {
val local = atomic(0)
fun testGetValue() {
val a = IntArithmetic()
a._x.value = 5
check(a._x.value == 5)
var aValue = a._x.value
check(aValue == 5)
check(a.x == 5)
local.value = 555
aValue = local.value
check(local.value == aValue)
}
fun testAtomicCallPlaces(): Boolean {
val a = IntArithmetic()
a._x.value = 5
a._x.compareAndSet(5, 42)
val res = a._x.compareAndSet(42, 45)
check(res)
check(a._x.compareAndSet(45, 77))
check(!a._x.compareAndSet(95, 77))
return a._x.compareAndSet(77, 88)
}
fun testInt() {
val a = IntArithmetic()
check(a.x == 0)
val update = 3
check(a._x.getAndSet(update) == 0)
check(a._x.compareAndSet(update, 8))
a._x.lazySet(1)
check(a.x == 1)
check(a._x.getAndSet(2) == 1)
check(a.x == 2)
check(a._x.getAndIncrement() == 2)
check(a.x == 3)
check(a._x.getAndDecrement() == 3)
check(a.x == 2)
check(a._x.getAndAdd(2) == 2)
check(a.x == 4)
check(a._x.addAndGet(3) == 7)
check(a.x == 7)
check(a._x.incrementAndGet() == 8)
check(a.x == 8)
check(a._x.decrementAndGet() == 7)
check(a.x == 7)
check(a._x.compareAndSet(7, 10))
}
fun testLong() {
val a = LongArithmetic()
check(a.z.value == 2424920024888888848)
a.z.lazySet(8424920024888888848)
check(a.z.value == 8424920024888888848)
check(a.z.getAndSet(8924920024888888848) == 8424920024888888848)
check(a.z.value == 8924920024888888848)
check(a.z.incrementAndGet() == 8924920024888888849) // fails
check(a.z.value == 8924920024888888849)
check(a.z.getAndDecrement() == 8924920024888888849)
check(a.z.value == 8924920024888888848)
check(a.z.getAndAdd(100000000000000000) == 8924920024888888848)
check(a.z.value == 9024920024888888848)
check(a.z.addAndGet(-9223372036854775807) == -198452011965886959)
check(a.z.value == -198452011965886959)
check(a.z.incrementAndGet() == -198452011965886958)
check(a.z.value == -198452011965886958)
check(a.z.decrementAndGet() == -198452011965886959)
check(a.z.value == -198452011965886959)
}
fun testBoolean() {
val a = BooleanArithmetic()
check(!a.x)
a._x.lazySet(true)
check(a.x)
check(a._x.getAndSet(true))
check(a._x.compareAndSet(true, false))
check(!a.x)
}
fun testReference() {
val a = ReferenceArithmetic()
a._x.value = "aaa"
check(a._x.value == "aaa")
a._x.lazySet("bb")
check(a._x.value == "bb")
check(a._x.getAndSet("ccc") == "bb")
check(a._x.value == "ccc")
}
}
fun box(): String {
val testClass = ArithmeticTest()
testClass.testGetValue()
if (!testClass.testAtomicCallPlaces()) return "testAtomicCallPlaces: FAILED"
testClass.testInt()
testClass.testLong()
testClass.testBoolean()
testClass.testReference()
return "OK"
}
@@ -0,0 +1,47 @@
import kotlinx.atomicfu.*
import kotlin.test.*
class ArrayInlineFunctionTest {
private val anyArr = atomicArrayOfNulls<Any?>(5)
private val refArr = atomicArrayOfNulls<Box>(5)
private data class Box(val n: Int)
fun testSetArrayElementValueInLoop() {
anyArr[0].loop { cur ->
assertTrue(anyArr[0].compareAndSet(cur, IntArray(5)))
return
}
}
private fun action(cur: Box?) = cur?.let { Box(cur.n * 10) }
fun testArrayElementUpdate() {
refArr[0].lazySet(Box(5))
refArr[0].update { cur -> cur?.let { Box(cur.n * 10) } }
assertEquals(refArr[0].value!!.n, 50)
}
fun testArrayElementGetAndUpdate() {
refArr[0].lazySet(Box(5))
assertEquals(refArr[0].getAndUpdate { cur -> action(cur) }!!.n, 5)
assertEquals(refArr[0].value!!.n, 50)
}
fun testArrayElementUpdateAndGet() {
refArr[0].lazySet(Box(5))
assertEquals(refArr[0].updateAndGet { cur -> action(cur) }!!.n, 50)
}
}
fun box(): String {
val testClass = ArrayInlineFunctionTest()
testClass.testSetArrayElementValueInLoop()
testClass.testArrayElementGetAndUpdate()
testClass.testArrayElementUpdate()
testClass.testArrayElementUpdateAndGet()
return "OK"
}
@@ -0,0 +1,97 @@
import kotlinx.atomicfu.*
import kotlin.test.*
class AtomicArrayTest {
fun testIntArray() {
val A = AtomicArrayClass()
check(A.intArr[0].compareAndSet(0, 3))
check(A.intArr[1].value == 0)
A.intArr[0].lazySet(5)
check(A.intArr[0].value + A.intArr[1].value + A.intArr[2].value == 5)
check(A.intArr[0].compareAndSet(5, 10))
check(A.intArr[0].getAndDecrement() == 10)
check(A.intArr[0].value == 9)
A.intArr[2].value = 2
check(A.intArr[2].value == 2)
check(A.intArr[2].compareAndSet(2, 34))
check(A.intArr[2].value == 34)
}
fun testLongArray() {
val A = AtomicArrayClass()
A.longArr[0].value = 2424920024888888848
check(A.longArr[0].value == 2424920024888888848)
A.longArr[0].lazySet(8424920024888888848)
check(A.longArr[0].value == 8424920024888888848)
val ac = A.longArr[0].value
A.longArr[3].value = ac
check(A.longArr[3].getAndSet(8924920024888888848) == 8424920024888888848)
check(A.longArr[3].value == 8924920024888888848)
val ac1 = A.longArr[3].value
A.longArr[4].value = ac1
check(A.longArr[4].incrementAndGet() == 8924920024888888849)
check(A.longArr[4].value == 8924920024888888849)
check(A.longArr[4].getAndDecrement() == 8924920024888888849)
check(A.longArr[4].value == 8924920024888888848)
A.longArr[4].value = 8924920024888888848
check(A.longArr[4].getAndAdd(100000000000000000) == 8924920024888888848)
val ac2 = A.longArr[4].value
A.longArr[1].value = ac2
check(A.longArr[1].value == 9024920024888888848)
check(A.longArr[1].addAndGet(-9223372036854775807) == -198452011965886959)
check(A.longArr[1].value == -198452011965886959)
check(A.longArr[1].incrementAndGet() == -198452011965886958)
check(A.longArr[1].value == -198452011965886958)
check(A.longArr[1].decrementAndGet() == -198452011965886959)
check(A.longArr[1].value == -198452011965886959)
}
fun testBooleanArray() {
val A = AtomicArrayClass()
check(!A.booleanArr[1].value)
A.booleanArr[1].compareAndSet(false, true)
A.booleanArr[0].lazySet(true)
check(!A.booleanArr[2].getAndSet(true))
check(A.booleanArr[0].value && A.booleanArr[1].value && A.booleanArr[2].value)
A.booleanArr[0].value = false
check(!A.booleanArr[0].value)
}
fun testRefArray() {
val A = AtomicArrayClass()
val a2 = ARef(2)
val a3 = ARef(3)
A.refArr[0].value = a2
check(A.refArr[0].value!!.n == 2)
check(A.refArr[0].compareAndSet(a2, a3))
check(A.refArr[0].value!!.n == 3)
val r0 = A.refArr[0].value
A.refArr[3].value = r0
check(A.refArr[3].value!!.n == 3)
val a = A.a.value
check(A.refArr[3].compareAndSet(a3, a))
}
}
class AtomicArrayClass {
val intArr = AtomicIntArray(10)
val longArr = AtomicLongArray(10)
val booleanArr = AtomicBooleanArray(10)
val refArr = atomicArrayOfNulls<ARef>(10)
val anyArr = atomicArrayOfNulls<Any?>(10)
val a = atomic(ARef(8))
}
data class ARef(val n: Int)
fun box(): String {
val testClass = AtomicArrayTest()
testClass.testIntArray()
testClass.testLongArray()
testClass.testBooleanArray()
testClass.testRefArray()
return "OK"
}
@@ -0,0 +1,115 @@
import kotlinx.atomicfu.*
import kotlin.test.*
class ExtensionsTest {
val a = atomic(0)
val l = atomic(0L)
val s = atomic<String?>(null)
val b = atomic(true)
fun testScopedFieldGetters() {
check(a.value == 0)
val update = 3
a.lazySet(update)
check(a.compareAndSet(update, 8))
a.lazySet(1)
check(a.value == 1)
check(a.getAndSet(2) == 1)
check(a.value == 2)
check(a.getAndIncrement() == 2)
check(a.value == 3)
check(a.getAndDecrement() == 3)
check(a.value == 2)
check(a.getAndAdd(2) == 2)
check(a.value == 4)
check(a.addAndGet(3) == 7)
check(a.value == 7)
check(a.incrementAndGet() == 8)
check(a.value == 8)
check(a.decrementAndGet() == 7)
check(a.value == 7)
check(a.compareAndSet(7, 10))
}
inline fun AtomicInt.intExtensionArithmetic() {
value = 0
check(value == 0)
val update = 3
lazySet(update)
check(compareAndSet(update, 8))
lazySet(1)
check(value == 1)
check(getAndSet(2) == 1)
check(value == 2)
check(getAndIncrement() == 2)
check(value == 3)
check(getAndDecrement() == 3)
check(value == 2)
check(getAndAdd(2) == 2)
check(value == 4)
check(addAndGet(3) == 7)
check(value == 7)
check(incrementAndGet() == 8)
check(value == 8)
check(decrementAndGet() == 7)
check(value == 7)
check(compareAndSet(7, 10))
check(compareAndSet(value, 55))
check(value == 55)
}
inline fun AtomicLong.longExtensionArithmetic() {
value = 2424920024888888848
check(value == 2424920024888888848)
lazySet(8424920024888888848)
check(value == 8424920024888888848)
check(getAndSet(8924920024888888848) == 8424920024888888848)
check(value == 8924920024888888848)
check(incrementAndGet() == 8924920024888888849) // fails
check(value == 8924920024888888849)
check(getAndDecrement() == 8924920024888888849)
check(value == 8924920024888888848)
check(getAndAdd(100000000000000000) == 8924920024888888848)
check(value == 9024920024888888848)
check(addAndGet(-9223372036854775807) == -198452011965886959)
check(value == -198452011965886959)
check(incrementAndGet() == -198452011965886958)
check(value == -198452011965886958)
check(decrementAndGet() == -198452011965886959)
check(value == -198452011965886959)
}
inline fun AtomicRef<String?>.refExtension() {
value = "aaa"
check(value == "aaa")
lazySet("bb")
check(value == "bb")
check(getAndSet("ccc") == "bb")
check(value == "ccc")
}
inline fun AtomicBoolean.booleanExtensionArithmetic() {
value = false
check(!value)
lazySet(true)
check(value)
check(getAndSet(true))
check(compareAndSet(value, false))
check(!value)
}
fun testExtension() {
a.intExtensionArithmetic()
l.longExtensionArithmetic()
s.refExtension()
b.booleanExtensionArithmetic()
}
}
fun box(): String {
val testClass = ExtensionsTest()
testClass.testScopedFieldGetters()
testClass.testExtension()
return "OK"
}
@@ -0,0 +1,32 @@
import kotlinx.atomicfu.*
import kotlin.test.*
class IndexArrayElementGetterTest {
private val clazz = AtomicArrayClass()
fun fib(a: Int): Int = if (a == 0 || a == 1) a else fib(a - 1) + fib(a - 2)
fun testIndexArrayElementGetting() {
clazz.intArr[8].value = 3
val i = fib(4)
val j = fib(5)
assertEquals(clazz.intArr[i + j].value, 3)
assertEquals(clazz.intArr[fib(4) + fib(5)].value, 3)
clazz.longArr[3].value = 100
assertEquals(clazz.longArr[fib(6) - fib(5)].value, 100)
assertEquals(clazz.longArr[(fib(6) + fib(4)) % 8].value, 100)
assertEquals(clazz.longArr[(fib(6) + fib(4)) % 8].value, 100)
assertEquals(clazz.longArr[(fib(4) + fib(5)) % fib(5)].value, 100)
}
class AtomicArrayClass {
val intArr = AtomicIntArray(10)
val longArr = AtomicLongArray(10)
}
}
fun box(): String {
val testClass = IndexArrayElementGetterTest()
testClass.testIndexArrayElementGetting()
return "OK"
}
@@ -0,0 +1,31 @@
import kotlinx.atomicfu.*
import kotlin.test.*
class InlineExtensionWithTypeParameterTest {
abstract class Segment<S : Segment<S>>(val id: Int)
class SemaphoreSegment(id: Int) : Segment<SemaphoreSegment>(id)
private inline fun <S : Segment<S>> AtomicRef<S>.foo(
id: Int,
startFrom: S
) {
startFrom.getSegmentId()
}
private inline fun <S : Segment<S>> S.getSegmentId(): Int {
var cur: S = this
return cur.id
}
fun testInlineExtensionWithTypeParameter() {
val s = SemaphoreSegment(0)
val sref = atomic(s)
sref.foo(0, s)
}
}
fun box(): String {
val testClass = InlineExtensionWithTypeParameterTest()
testClass.testInlineExtensionWithTypeParameter()
return "OK"
}
@@ -0,0 +1,57 @@
import kotlinx.atomicfu.*
import kotlin.test.*
class LockFreeIntBitsTest {
fun testBasic() {
val bs = LockFreeIntBits()
check(!bs[0])
check(bs.bitSet(0))
check(bs[0])
check(!bs.bitSet(0))
check(!bs[1])
check(bs.bitSet(1))
check(bs[1])
check(!bs.bitSet(1))
check(!bs.bitSet(0))
check(bs[0])
check(bs.bitClear(0))
check(!bs.bitClear(0))
check(bs[1])
}
}
class LockFreeIntBits {
private val bits = atomic(0)
private fun Int.mask() = 1 shl this
operator fun get(index: Int): Boolean = bits.value and index.mask() != 0
// User-defined private inline function
private inline fun bitUpdate(check: (Int) -> Boolean, upd: (Int) -> Int): Boolean {
bits.update {
if (check(it)) return false
upd(it)
}
return true
}
fun bitSet(index: Int): Boolean {
val mask = index.mask()
return bitUpdate({ it and mask != 0 }, { it or mask })
}
fun bitClear(index: Int): Boolean {
val mask = index.mask()
return bitUpdate({ it and mask == 0 }, { it and mask.inv() })
}
}
fun box(): String {
val testClass = LockFreeIntBitsTest()
testClass.testBasic()
return "OK"
}
@@ -0,0 +1,61 @@
import kotlinx.atomicfu.*
import kotlin.test.*
class LockFreeLongCounterTest {
private inline fun testWith(g: LockFreeLongCounter.() -> Long) {
val c = LockFreeLongCounter()
check(c.g() == 0L)
check(c.increment() == 1L)
check(c.g() == 1L)
check(c.increment() == 2L)
check(c.g() == 2L)
}
fun testBasic() = testWith { get() }
fun testGetInner() = testWith { getInner() }
fun testAdd2() {
val c = LockFreeLongCounter()
c.add2()
check(c.get() == 2L)
c.add2()
check(c.get() == 4L)
}
fun testSetM2() {
val c = LockFreeLongCounter()
c.setM2()
check(c.get() == -2L)
}
}
class LockFreeLongCounter {
private val counter = atomic(0L)
fun get(): Long = counter.value
fun increment(): Long = counter.incrementAndGet()
fun add2() = counter.getAndAdd(2)
fun setM2() {
counter.value = -2L // LDC instruction here
}
fun getInner(): Long = Inner().getFromOuter()
// testing how an inner class can get access to it
private inner class Inner {
fun getFromOuter(): Long = counter.value
}
}
fun box(): String {
val testClass = LockFreeLongCounterTest()
testClass.testBasic()
testClass.testAdd2()
testClass.testSetM2()
testClass.testGetInner()
return "OK"
}
@@ -0,0 +1,55 @@
import kotlinx.atomicfu.*
import kotlin.test.*
class LockFreeQueueTest {
fun testBasic() {
val q = LockFreeQueue()
check(q.dequeue() == -1)
q.enqueue(42)
check(q.dequeue() == 42)
check(q.dequeue() == -1)
q.enqueue(1)
q.enqueue(2)
check(q.dequeue() == 1)
check(q.dequeue() == 2)
check(q.dequeue() == -1)
}
}
// MS-queue
public class LockFreeQueue {
private val head = atomic(Node(0))
private val tail = atomic(head.value)
private class Node(val value: Int) {
val next = atomic<Node?>(null)
}
public fun enqueue(value: Int) {
val node = Node(value)
tail.loop { curTail ->
val curNext = curTail.next.value
if (curNext != null) {
tail.compareAndSet(curTail, curNext)
return@loop
}
if (curTail.next.compareAndSet(null, node)) {
tail.compareAndSet(curTail, node)
return
}
}
}
public fun dequeue(): Int {
head.loop { curHead ->
val next = curHead.next.value ?: return -1
if (head.compareAndSet(curHead, next)) return next.value
}
}
}
fun box(): String {
val testClass = LockFreeQueueTest()
testClass.testBasic()
return "OK"
}
@@ -0,0 +1,71 @@
import kotlinx.atomicfu.*
import kotlin.test.*
class LockFreeStackTest {
fun testClear() {
val s = LockFreeStack<String>()
check(s.isEmpty())
s.pushLoop("A")
check(!s.isEmpty())
s.clear()
check(s.isEmpty())
}
fun testPushPopLoop() {
val s = LockFreeStack<String>()
check(s.isEmpty())
s.pushLoop("A")
check(!s.isEmpty())
check(s.popLoop() == "A")
check(s.isEmpty())
}
fun testPushPopUpdate() {
val s = LockFreeStack<String>()
check(s.isEmpty())
s.pushUpdate("A")
check(!s.isEmpty())
check(s.popUpdate() == "A")
check(s.isEmpty())
}
}
class LockFreeStack<T> {
private val top = atomic<Node<T>?>(null)
private class Node<T>(val value: T, val next: Node<T>?)
fun isEmpty() = top.value == null
fun clear() { top.value = null }
fun pushLoop(value: T) {
top.loop { cur ->
val upd = Node(value, cur)
if (top.compareAndSet(cur, upd)) return
}
}
fun popLoop(): T? {
top.loop { cur ->
if (cur == null) return null
if (top.compareAndSet(cur, cur.next)) return cur.value
}
}
fun pushUpdate(value: T) {
top.update { cur -> Node(value, cur) }
}
fun popUpdate(): T? =
top.getAndUpdate { cur -> cur?.next } ?.value
}
fun box(): String {
val testClass = LockFreeStackTest()
testClass.testClear()
testClass.testPushPopLoop()
testClass.testPushPopUpdate()
return "OK"
}
@@ -0,0 +1,29 @@
import kotlinx.atomicfu.*
import kotlin.test.*
class LockTest {
private val inProgressLock = atomic(false)
fun testLock() {
var result = ""
if (inProgressLock.tryAcquire()) {
result = "OK"
}
assertEquals("OK", result)
}
}
// This function will be removed by transformer
@Suppress("NOTHING_TO_INLINE")
private inline fun AtomicBoolean.tryAcquire(): Boolean = compareAndSet(false, true)
// This function is here to test if the Kotlin metadata still consistent after transform
// It is used in ReflectionTest, DO NOT REMOVE
@Suppress("UNUSED_PARAMETER")
fun <AA, BB : Number> String.reflectionTest(mapParam: Map<in AA, BB>): List<BB> = error("no impl")
fun box(): String {
val testClass = LockTest()
testClass.testLock()
return "OK"
}
@@ -0,0 +1,82 @@
import kotlinx.atomicfu.*
import kotlin.test.*
class LoopTest {
private val a = atomic(0)
private val r = atomic<A>(A("aaaa"))
private val rs = atomic<String>("bbbb")
private class A(val s: String)
private inline fun casLoop(to: Int): Int {
a.loop { cur ->
if (a.compareAndSet(cur, to)) return a.value
return 777
}
}
private inline fun casLoopExpression(to: Int): Int = a.loop { cur ->
if (a.compareAndSet(cur, to)) return a.value
return 777
}
private inline fun AtomicInt.extensionLoop(to: Int): Int {
loop { cur ->
if (compareAndSet(cur, to)) return value
return 777
}
}
private inline fun AtomicInt.extensionLoopExpression(to: Int): Int = loop { cur ->
lazySet(cur + 10)
return if (compareAndSet(cur, to)) value else incrementAndGet()
}
private inline fun AtomicInt.extensionLoopMixedReceivers(first: Int, second: Int): Int {
loop { cur ->
compareAndSet(cur, first)
a.compareAndSet(first, second)
return value
}
}
private inline fun AtomicInt.extensionLoopRecursive(to: Int): Int {
loop { cur ->
compareAndSet(cur, to)
a.extensionLoop(5)
return value
}
}
fun testIntExtensionLoops() {
assertEquals(5, casLoop(5))
assertEquals(6, casLoopExpression(6))
assertEquals(66, a.extensionLoop(66))
assertEquals(77, a.extensionLoopExpression(777))
assertEquals(99, a.extensionLoopMixedReceivers(88, 99))
assertEquals(5, a.extensionLoopRecursive(100))
}
private inline fun AtomicRef<A>.casLoop(to: String): String = loop { cur ->
if (compareAndSet(cur, A(to))) {
val res = value.s
return "${res}_AtomicRef<A>"
}
}
private inline fun AtomicRef<String>.casLoop(to: String): String = loop { cur ->
if (compareAndSet(cur, to)) return "${value}_AtomicRef<String>"
}
fun testDeclarationWithEqualNames() {
check(r.casLoop("kk") == "kk_AtomicRef<A>")
check(rs.casLoop("pp") == "pp_AtomicRef<String>")
}
}
fun box(): String {
val testClass = LoopTest()
testClass.testIntExtensionLoops()
testClass.testDeclarationWithEqualNames()
return "OK"
}
@@ -0,0 +1,30 @@
import kotlinx.atomicfu.*
import kotlin.test.*
class MultiInitTest {
fun testBasic() {
val t = MultiInit()
check(t.incA() == 1)
check(t.incA() == 2)
check(t.incB() == 1)
check(t.incB() == 2)
}
}
class MultiInit {
private val a = atomic(0)
private val b = atomic(0)
fun incA() = a.incrementAndGet()
fun incB() = b.incrementAndGet()
companion object {
fun foo() {} // just to force some clinit in outer file
}
}
fun box(): String {
val testClass = MultiInitTest()
testClass.testBasic()
return "OK"
}
@@ -0,0 +1,27 @@
import kotlinx.atomicfu.*
import kotlin.test.*
class ParameterizedInlineFunExtensionTest {
private inline fun <S> AtomicRef<S>.foo(res1: S, res2: S, foo: (S) -> S): S {
val res = bar(res1, res2)
return res
}
private inline fun <S> AtomicRef<S>.bar(res1: S, res2: S): S {
return res2
}
private val tail = atomic("aaa")
fun testClose() {
val res = tail.foo("bbb", "ccc") { s -> s }
assertEquals("ccc", res)
}
}
fun box(): String {
val testClass = ParameterizedInlineFunExtensionTest()
testClass.testClose()
return "OK"
}
@@ -0,0 +1,34 @@
import kotlinx.atomicfu.*
import kotlinx.atomicfu.locks.*
import kotlin.test.*
class PropertyDeclarationTest {
private val a: AtomicInt
private val head: AtomicRef<String>
private val lateIntArr: AtomicIntArray
private val lateRefArr: AtomicArray<String?>
private val lock: ReentrantLock
init {
a = atomic(0)
head = atomic("AAA")
lateIntArr = AtomicIntArray(55)
lateRefArr = atomicArrayOfNulls<String?>(10)
lock = reentrantLock()
}
fun test() {
assertEquals(0, a.value)
check(head.compareAndSet("AAA", "BBB"))
assertEquals("BBB", head.value)
assertEquals(0, lateIntArr[35].value)
assertEquals(null, lateRefArr[5].value)
assertEquals(null, lock)
}
}
fun box(): String {
val testClass = PropertyDeclarationTest()
testClass.test()
return "OK"
}
@@ -0,0 +1,20 @@
import kotlinx.atomicfu.locks.*
import kotlin.test.*
class ReentrantLockTest {
private val lock = reentrantLock()
private var state = 0
fun testLockField() {
lock.withLock {
state = 1
}
assertEquals(1, state)
}
}
fun box(): String {
val testClass = ReentrantLockTest()
testClass.testLockField()
return "OK"
}
@@ -0,0 +1,45 @@
import kotlinx.atomicfu.*
import kotlin.test.*
class AA(val value: Int) {
val b = B(value + 1)
val c = C(D(E(value + 1)))
fun updateToB(affected: Any): Boolean {
(affected as AtomicState).state.compareAndSet(this, b)
return (affected.state.value is B && (affected.state.value as B).value == value + 1)
}
fun manyProperties(affected: Any): Boolean {
(affected as AtomicState).state.compareAndSet(this, c.d.e)
return (affected.state.value is E && (affected.state.value as E).x == value + 1)
}
}
class B (val value: Int)
class C (val d: D)
class D (val e: E)
class E (val x: Int)
class AtomicState(value: Any) {
val state = atomic<Any?>(value)
}
class ScopeTest {
fun scopeTest() {
val a = AA(0)
val affected: Any = AtomicState(a)
check(a.updateToB(affected))
val a1 = AA(0)
val affected1: Any = AtomicState(a1)
check(a1.manyProperties(affected1))
}
}
fun box(): String {
val testClass = ScopeTest()
testClass.scopeTest()
return "OK"
}
@@ -0,0 +1,36 @@
import kotlinx.atomicfu.*
import kotlin.test.*
class SimpleLockTest {
fun withLock() {
val lock = SimpleLock()
val result = lock.withLock {
"OK"
}
assertEquals("OK", result)
}
}
class SimpleLock {
private val _locked = atomic(0)
fun <T> withLock(block: () -> T): T {
// this contrieves construct triggers Kotlin compiler to reuse local variable slot #2 for
// the exception in `finally` clause
try {
_locked.loop { locked ->
check(locked == 0)
if (!_locked.compareAndSet(0, 1)) return@loop // continue
return block()
}
} finally {
_locked.value = 0
}
}
}
fun box(): String {
val testClass = SimpleLockTest()
testClass.withLock()
return "OK"
}
@@ -0,0 +1,21 @@
import kotlinx.atomicfu.locks.*
import kotlin.test.*
class SynchronizedObjectTest : SynchronizedObject() {
fun testSync() {
val result = synchronized(this) { bar() }
assertEquals("OK", result)
}
private fun bar(): String =
synchronized(this) {
"OK"
}
}
fun box(): String {
val testClass = SynchronizedObjectTest()
testClass.testSync()
return "OK"
}
@@ -0,0 +1,178 @@
import kotlinx.atomicfu.*
import kotlin.test.*
private val a = atomic(0)
private val b = atomic(2424920024888888848)
private val c = atomic(true)
private val abcNode = atomic(ANode(BNode(CNode(8))))
private val any = atomic<Any?>(null)
private val intArr = AtomicIntArray(3)
private val longArr = AtomicLongArray(5)
private val booleanArr = AtomicBooleanArray(4)
private val refArr = atomicArrayOfNulls<ANode<BNode<CNode>>>(5)
private val anyRefArr = atomicArrayOfNulls<Any>(10)
private val stringAtomicNullArr = atomicArrayOfNulls<String>(10)
class TopLevelPrimitiveTest {
fun testTopLevelInt() {
a.value
check(a.value == 0)
check(a.getAndSet(3) == 0)
check(a.compareAndSet(3, 8))
a.lazySet(1)
check(a.value == 1)
check(a.getAndSet(2) == 1)
check(a.value == 2)
check(a.getAndIncrement() == 2)
check(a.value == 3)
check(a.getAndDecrement() == 3)
check(a.value == 2)
check(a.getAndAdd(2) == 2)
check(a.value == 4)
check(a.addAndGet(3) == 7)
check(a.value == 7)
check(a.incrementAndGet() == 8)
check(a.value == 8)
check(a.decrementAndGet() == 7)
check(a.value == 7)
a.compareAndSet(7, 10)
}
fun testTopLevelLong() {
check(b.value == 2424920024888888848)
b.lazySet(8424920024888888848)
check(b.value == 8424920024888888848)
check(b.getAndSet(8924920024888888848) == 8424920024888888848)
check(b.value == 8924920024888888848)
check(b.incrementAndGet() == 8924920024888888849)
check(b.value == 8924920024888888849)
check(b.getAndDecrement() == 8924920024888888849)
check(b.value == 8924920024888888848)
check(b.getAndAdd(100000000000000000) == 8924920024888888848)
check(b.value == 9024920024888888848)
check(b.addAndGet(-9223372036854775807) == -198452011965886959)
check(b.value == -198452011965886959)
check(b.incrementAndGet() == -198452011965886958)
check(b.value == -198452011965886958)
check(b.decrementAndGet() == -198452011965886959)
check(b.value == -198452011965886959)
}
fun testTopLevelBoolean() {
check(c.value)
c.lazySet(false)
check(!c.value)
check(!c.getAndSet(true))
check(c.compareAndSet(true, false))
check(!c.value)
}
fun testTopLevelRef() {
check(abcNode.value.b.c.d == 8)
val newNode = ANode(BNode(CNode(76)))
check(abcNode.getAndSet(newNode).b.c.d == 8)
check(abcNode.value.b.c.d == 76)
val l = IntArray(4){i -> i}
any.lazySet(l)
check((any.value as IntArray)[2] == 2)
}
fun testTopLevelArrayOfNulls() {
check(stringAtomicNullArr[0].value == null)
check(stringAtomicNullArr[0].compareAndSet(null, "aa"))
stringAtomicNullArr[1].lazySet("aa")
check(stringAtomicNullArr[0].value == stringAtomicNullArr[1].value)
}
}
class TopLevelArrayTest {
fun testIntArray() {
check(intArr[0].compareAndSet(0, 3))
check(intArr[1].value == 0)
intArr[0].lazySet(5)
check(intArr[0].value + intArr[1].value + intArr[2].value == 5)
check(intArr[0].compareAndSet(5, 10))
check(intArr[0].getAndDecrement() == 10)
check(intArr[0].value == 9)
intArr[2].value = 2
check(intArr[2].value == 2)
check(intArr[2].compareAndSet(2, 34))
check(intArr[2].value == 34)
}
fun testLongArray() {
longArr[0].value = 2424920024888888848
check(longArr[0].value == 2424920024888888848)
longArr[0].lazySet(8424920024888888848)
check(longArr[0].value == 8424920024888888848)
val ac = longArr[0].value
longArr[3].value = ac
check(longArr[3].getAndSet(8924920024888888848) == 8424920024888888848)
check(longArr[3].value == 8924920024888888848)
val ac1 = longArr[3].value
longArr[4].value = ac1
check(longArr[4].incrementAndGet() == 8924920024888888849)
check(longArr[4].value == 8924920024888888849)
check(longArr[4].getAndDecrement() == 8924920024888888849)
check(longArr[4].value == 8924920024888888848)
longArr[4].value = 8924920024888888848
check(longArr[4].getAndAdd(100000000000000000) == 8924920024888888848)
val ac2 = longArr[4].value
longArr[1].value = ac2
check(longArr[1].value == 9024920024888888848)
check(longArr[1].addAndGet(-9223372036854775807) == -198452011965886959)
check(longArr[1].value == -198452011965886959)
check(longArr[1].incrementAndGet() == -198452011965886958)
check(longArr[1].value == -198452011965886958)
check(longArr[1].decrementAndGet() == -198452011965886959)
check(longArr[1].value == -198452011965886959)
}
fun testBooleanArray() {
check(!booleanArr[1].value)
booleanArr[1].compareAndSet(false, true)
booleanArr[0].lazySet(true)
check(!booleanArr[2].getAndSet(true))
check(booleanArr[0].value && booleanArr[1].value && booleanArr[2].value)
}
@Suppress("UNCHECKED_CAST")
fun testRefArray() {
val a2 = ANode(BNode(CNode(2)))
val a3 = ANode(BNode(CNode(3)))
refArr[0].value = a2
check(refArr[0].value!!.b.c.d == 2)
check(refArr[0].compareAndSet(a2, a3))
check(refArr[0].value!!.b.c.d == 3)
val r0 = refArr[0].value
refArr[3].value = r0
check(refArr[3].value!!.b.c.d == 3)
val a = abcNode.value
check(refArr[3].compareAndSet(a3, a))
}
}
data class ANode<T>(val b: T)
data class BNode<T>(val c: T)
data class CNode(val d: Int)
fun box(): String {
val primitiveTest = TopLevelPrimitiveTest()
primitiveTest.testTopLevelInt()
primitiveTest.testTopLevelLong()
primitiveTest.testTopLevelBoolean()
primitiveTest.testTopLevelRef()
primitiveTest.testTopLevelArrayOfNulls()
val arrayTest = TopLevelArrayTest()
arrayTest.testIntArray()
arrayTest.testLongArray()
arrayTest.testBooleanArray()
arrayTest.testRefArray()
return "OK"
}
@@ -0,0 +1,54 @@
import kotlinx.atomicfu.*
import kotlin.test.*
private val topLevelS = atomic<Any>(arrayOf("A", "B"))
class UncheckedCastTest {
private val s = atomic<Any>("AAA")
private val bs = atomic<Any?>(null)
@Suppress("UNCHECKED_CAST")
fun testAtomicValUncheckedCast() {
assertEquals((s as AtomicRef<String>).value, "AAA")
bs.lazySet(arrayOf(arrayOf(Box(1), Box(2))))
assertEquals((bs as AtomicRef<Array<Array<Box>>>).value[0]!![0].b * 10, 10)
}
@Suppress("UNCHECKED_CAST")
fun testTopLevelValUnchekedCast() {
assertEquals((topLevelS as AtomicRef<Array<String>>).value[1], "B")
}
private data class Box(val b: Int)
@Suppress("NOTHING_TO_INLINE", "UNCHECKED_CAST")
private inline fun <T> AtomicRef<T>.getString(): String =
(this as AtomicRef<String>).value
fun testInlineFunc() {
assertEquals("AAA", s.getString())
}
private val a = atomicArrayOfNulls<Any?>(10)
fun testArrayValueUncheckedCast() {
a[0].value = "OK"
@Suppress("UNCHECKED_CAST")
assertEquals("OK", (a[0] as AtomicRef<String>).value)
}
fun testArrayValueUncheckedCastInlineFunc() {
a[0].value = "OK"
assertEquals("OK", a[0].getString())
}
}
fun box(): String {
val testClass = UncheckedCastTest()
testClass.testTopLevelValUnchekedCast()
testClass.testArrayValueUncheckedCast()
testClass.testArrayValueUncheckedCastInlineFunc()
testClass.testAtomicValUncheckedCast()
testClass.testInlineFunc()
return "OK"
}