I’m using a `StableRef` to allow a `frozen` object...
# kotlin-native
s
I’m using a
StableRef
to allow a
frozen
object to hold onto a non-frozen object, so that I can create an object that I can create a wrapping object that provides an interface that’s responsible for ensuring access on the right thread. This creates a memory leak though, as
StableRef
has to be `dispose`ed when it’s done. Is there any way to get around disposing
StableRef
so that I don’t have to add a
dispose()
fun on every single one of these interfaces? Is there another way to do this without using
StableRef
?
d
Are you passing the
StableRef
across threads? That isn't supported behaviour. Objects have to be explicitly transferred. You pretty much have to use
DetachedObjectGraph
.
k
@kpgalligan based stately iso collections on this usage of
StableRef
d
DetachedObjectGraph
still has the issue of having to dispose it. It's not really possible to get around this issue because you are acquiring a strong reference dynamically.
k
I prefer to make them
AutoCloseable
if there needs to be any cleanup
then you can
use
them
d
Is
AutoCloseable
now available on Native?
s
I’m not using
StableRef
across threads. I’m manually ensuring that I only access it on the correct thread.
Copy code
private class AbTestRepositoryImpl : AbTestRepository {
    private val delegate = StableRef.create(AbTestRepositoryBase())

    init {
        freeze()
    }

    override suspend fun getExperimentGroup(experimentName: String): String = withContext(dispatcher) {
        delegate.get().getExperimentGroup(experimentName)
    }
}
k
hmm, i thought it was, but appears not
s
I need something that can auto close when an object is collected by the native GC, or a way to intercept the GC event and close when the wrapping object is collected. Or I need a solution that doesn’t require closing.
d
I think you should rely on the Kotlin runtime to ensure the object is accessed on the right thread, instead of doing it yourself. (Of course I'm not sure if this is feasible for you use case)
s
I’m writing out a compiler plugin that would be a much nicer way to expose asyncronous interfaces that could be called from any thread thread.
k
the runtime doesn't control how objects are accessed (or via which thread). it only verifies.
the native-mt coroutines helps clean this up, but it doesn't remove freezing
but you have channels and flows for inter-thread communication, which can be frozen
however, whatever you send through them will also get frozen
s
@Kris Wong I’m pretty familiar with how this all works. That’s the problem that I’m encountering. I have an asynchronous interface that I want to be able to share between threads. It delegates to an implementation that always runs on a single thread. I want the asyncronous interface to ensure that the call is transferred to the right thread.
k
when I did something similar to that, I created a child interface of my mutable object called
ThreadedAccessor
, which could be frozen, and it send messages to a
Channel
s
Thus - the asynchronous interface must be
frozen
, but I don’t want the delegate that it uses to be frozen. I want that to be able to access state without having to use
atomics
because the wrapping interface ensures that it’s only ever touched by a single thread.
Make sense?
k
yeah, that's quite similar to what I am doing
but I have the parent/child relationship reversed
s
so is my only solution to add a
dispose
method? That’d be pretty crappy.
k
what's the lifetime of your objects?
and is there no obvious point at which are done with the stable ref that a dispose makes sense?
s
well - in this case it’s a singleton, so it’s fine
but there are other cases that I have to solve for that aren’t singletons