Closed kuhnroyal closed 5 years ago
"Adding query listeners"
Adding a listener on native means it'll freeze the listener you pass in. Listeners in general on native absolutely work, with the caveat that the listener will get frozen when it's passed in (as well as anything it references).
"or using the coroutine extension, which uses query listeners"
I haven't worked on the coroutine extension, but native support should be removed as it won't work properly until there's a multithreaded coroutines implementation for native.
For now, don't attempt to use Flow and native together, unless everything is happening in the same thread (presumably the main one). You can use listeners, but (again) be aware that your listener and everything it touches is frozen.
Live example of multiplatform with a reactive architecture using SqlDelight: https://github.com/touchlab/DroidconKotlin/
Basic pub/sub: https://github.com/touchlab/DroidconKotlin/blob/master/sessionize/lib/src/commonMain/kotlin/co/touchlab/sessionize/db/QueryPub.kt
Experimental example using Badoo's Reaktive: https://github.com/touchlab/DroidconKotlin/blob/kpg/reaktive/sessionize/lib/src/commonMain/kotlin/co/touchlab/sessionize/reaktive/ReaktiveExtensions.kt
Watching the commits to Kotlin/Native proper, there is movement on the basis for MT coroutines, and when that lands a lot of these other frameworks will kind of wither, but for today you need to hand roll things a bit.
Closing. In summary, until Multithreaded coroutines, can't use coroutines or we'd need to refactor how listeners work and (probably) live in the main thread.
As a workaround can we use the ThreadLocalRef here?
The modified logic would look like:
fun <T : Any> Query<T>.asFlow(): Flow<Query<T>> = callbackFlow<Query<T>> {
// using ThreadLocalRef as Query.Listener and all references in it get frozen on native
// May be removed once fixed in SQLDelight https://github.com/square/sqldelight/issues/1390
val onNextRef: ThreadLocalRef<() -> Unit> = ThreadLocalRef()
onNextRef.value = {
offer(this@asFlow)
}
val listener = object : Query.Listener {
override fun queryResultsChanged() {
onNextRef.value?.invoke()
}
}
addListener(listener)
offer(this@asFlow)
awaitClose {
removeListener(listener)
onNextRef.remove()
}
}.conflate()
ThreadLocalRef
logic helped .. had to make it actual/expected with this implementation for iOS.
Adding query listeners or using the coroutine extension, which uses query listeners, fails on native/iOS. Kotlin 1.3.40 and the current 1.2.0-SNAPSHOT
The cause seems to be
kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen kotlinx.atomicfu.AtomicRef
.Any ideas or workarounds?