Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AtomicRef<Int>.compareAndSet always fails when value >= 128 on macOS and iOS #369

Open
paxbun opened this issue Nov 23, 2023 · 5 comments
Open
Assignees
Labels

Comments

@paxbun
Copy link

paxbun commented Nov 23, 2023

I'm using kotlinx-atomicfu 0.22.0. I was having a problem where my program halts at AtomicRef<Int>.getAndUpdate, and I found that AtomicRef<Int>.compareAndSet always fails when the value >= 128 on macOS and iOS, both for JVM and native. For example:

for (i in 0 until 1024) {
    val a = kotlinx.atomicfu.atomic<Int>(i)
    print("${i}: ${a.compareAndSet(i, 8192)}")
}

run result:

0 true
...
127 true
128 false
...
1023 false

This does not happen when I use AtomicInt instead of AtomicRef<Int>.

for (i in 0 until 1024) {
    val a = kotlinx.atomicfu.atomic(i)
    println("${i}: ${a.compareAndSet(i, 8192)}")
}

run result:

0 true
...
127 true
128 true
...
1023 true
@paxbun paxbun changed the title AtomicRef<Int>.compareAndSet fails when value >= 128 on macOS and iOS AtomicRef<Int>.compareAndSet fails when value >= 128 on macOS and iOS Nov 23, 2023
@paxbun paxbun changed the title AtomicRef<Int>.compareAndSet fails when value >= 128 on macOS and iOS AtomicRef<Int>.compareAndSet always fails when value >= 128 on macOS and iOS Nov 23, 2023
@paxbun
Copy link
Author

paxbun commented Nov 23, 2023

@paxbun
Copy link
Author

paxbun commented Nov 23, 2023

Found that kotlin.concurrent.AtomicReference<Int> (not kotlinx) has the same problem. Create a fresh multiplatform project, set the Kotlin version to 1.9.20, run the following, and you'll get the same result.

import kotlin.concurrent.AtomicReference

fun main() {
    repeat(129) {
        val a = AtomicReference(it)
        val result = a.compareAndSet(it, 8192)
        println("$it: $result")
    }
}

@paxbun
Copy link
Author

paxbun commented Nov 23, 2023

Is it an expected behavior and an imitation of JVM's Integer pooling? I also found that compareAndSet always fails when value < -128.

@mvicsokolova mvicsokolova self-assigned this Nov 23, 2023
@qwwdfsad
Copy link
Member

qwwdfsad commented Feb 1, 2024

It seems like we need a proper inspection or compiler diagnostic here.
AtomicRef<Int> is as dangerous and tricky as Java's AtomicReference<Integer> -- it leaks the fact that boxed integer values may or may not be cached, and atomic ref (rightfully) uses identity equals and fails here.

So I'd say the behaviour is as intended, but we need a proper diagnostic to prevent such a bug (note: it's also reproducible with other primitives including booleans)

@qwwdfsad
Copy link
Member

qwwdfsad commented Feb 4, 2024

Posted the solution internally, re-posting here:

public fun <T> atomic(initial: T): AtomicRef<T> = atomic(initial, TraceBase.None)
public fun atomic(initial: Int): AtomicInt = atomic(initial, TraceBase.None)
@JvmName("atomicInt")
@Suppress("FINAL_UPPER_BOUND")
public fun <T : Int> atomic(initial: T): AtomicInt = atomic(initial.toInt(), TraceBase.None)

private val atomic = atomic<Int>(0)

@Test
fun foo() {
    atomic.value = 129
    println(atomic.compareAndSet(129, 130)) // true
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants