Hi everyone, I wonder if there is any workaround t...
# kotlin-native
n
Hi everyone, I wonder if there is any workaround to the following problem. I am creating a
StableRef
and passing the long address around to be used later. In some edge cases, this can happen after the stable ref is disposed, but there’s no way to catch the exception! •
asStableRef
doesn’t throw •
StableRef.get
doesn’t throw • dereferencing the result doesn’t throw in the Kotlin runtime, I get a segmentation fault
Copy code
class Handle {
    val ref = StableRef.create(this)
    val address = ref.asCPointer().toLong()
    fun doSomething() { ... }
    fun dispose() { ref.dispose() }
    companion object {
        fun doSomething(address: Long) {
            val handle = address.toCPointer<CPointed>()!!.asStableRef<Handle>().get()
            handle.doSomething()
        }
    }
}
k
It’s likely because the stable ref being disposed allows the memory in K/N to be rearranged. Therefore trying to dereference something which is essentially junk memory would indeed result in a segfault.
You should strive to ensure you’re not unwrapping stable refs after they’ve been disposed.
n
It’s unfortunate. I find it strange that the runtime allows me to have a garbage
Handle
, I’d rather have
get()
throw? I don’t see any way to ensure unwrapping valid stable refs only, that wouldn’t have a big impact on performance
k
You're at the boundary of the Kotlin runtime and the C ABI. Safety isn't guaranteed. I'm not sure Jetbrains could do what you want by throwing, but it's worth filing an issue while it's still experimental.
👍 1
n
Thanks Kevin!
l
I usually create a boolean in the Kotlin class (your Handle class in this case) that tracks whether the ref has been disposed, then throw an exception myself if the boolean indicates that the ref has been disposed.
n
In my case the Handle is what's being passed around through stable ref. I can't create a wrapper class either, I need everything to be accessible through the long address. The other solution that comes to mind is keeping information in a global map protected by a lock), which sucks. Or just redesigning all involved pieces with this obstacle in mind. I think I'll have to do that
k
Create a wrapper stable ref thing
Copy code
class MyStableRef<T>(private val delegate: StableRef<T>) {
  private var disposed: Boolean = false

  fun dispose() {
    check(!disposed)
    delegate.dispose()
    disposed = true
  }
}
n
Yeah but it doesn’t work as is because I need a static function with the signature
fun doSomething(address: Long)
. it is called by an external environment (JVM, through JNI) so I can’t pass KN objects, only the stable address - any wrapper would have to be stableref-ed itself. But I think I just got some ideas that I can play with. It’s funny how simply describing a problem to someone else often brings new ideas for a solution.
l
I would have the Handle class provide methods that wrap the C equivalent, so
doSomething(address)
becomes
handle.doSomething()
. Then the handle class checks for the ref to be non-null. This limits how much interaction there is between Kotlin and C, and provides nice boundaries to ensure memory safety.