0

I've noticed some unexpected behavior with a local variable in a function for Kotlin/Java. The following function was called concurrently several times, and sometimes the value of the variable temp is false instead of true.

What's more peculiar is that, I've verified that temp is set to true in every function invocation, but for some, it turns back to false in onTerminate.

My expectation was that for each function invocation, a new memory address will be used for the temp variable as long as the temp variable is not garbage collected. Since the variable is included in the callback/listener Completable, shouldn't it stay in memory, and so temp should always be true in the onTerminate section? What is going on here?

fun getCompletable() {
    var temp = false
    return Completable.create {
        acquireLock()
        temp = true
        ...
    }.onTerminate {
        println(temp) // temp is sometimes true and sometimes false
    }
}
detcle
  • 35
  • 4
  • Sounds like a race condition; this isn't going to be a memory sharing issue. – Louis Wasserman Oct 08 '22 at 00:08
  • @LouisWasserman Thanks for your reply. The above code is pretty much the entirety of the code. Given the above code, isn't a race condition with `temp` impossible since `temp` is not modified otherwise anywhere? – detcle Oct 08 '22 at 00:17
  • 2
    `temp` is only set to `true` when the `onSubscribe` callback you pass to `create` is invoked - if you never subscribe to the `Completable`, it will never run, so `temp` will still be `false` when the `Completable` is terminated. – superhawk610 Oct 08 '22 at 00:26
  • @superhawk610 That is what I initially thought. However, I've verified that temp is set to true in every function invocation, but for some of the invocations, it turns back to false in onTerminate. Which is why I suspected something with memory address being reused, which seems unlikely but I don't know why else it would happen. – detcle Oct 08 '22 at 00:31
  • Can you update your code sample to include how `getCompletable` is being called? – superhawk610 Oct 08 '22 at 00:35
  • 2
    I'm not sure the java tag is relevant; java does not allow you to mutate any local that is used inside a closure. I don't know how kotlin implement this, but if it just hoists into heap, then you're seeing what always happens when you write/read to the same field from 2 different threads: Coin flips as to whether you can observe the change, or not. That's _WHY_ java doesn't allow you to do it, I assume Kotlin decided that was annoying and made an error in that determination that shows up here. – rzwitserloot Oct 08 '22 at 01:27
  • 2
    The problem is that the captured `temp` is not volatile. See the answer here: https://stackoverflow.com/questions/42479009/kotlin-local-variable-thread-safety – broot Oct 08 '22 at 09:58
  • @broot Thanks for the answer, this is the correct explanation I believe. I had checked the decompiled java code while debugging, and indeed they were ObjectRefs. – detcle Oct 08 '22 at 20:32

0 Answers0