Nobody knows how to prevent freeze propagation in ...
# coroutines
r
Nobody knows how to prevent freeze propagation in coroutines? Like, here the
http.get
will freeze the coroutine it runs in and I’m trying to prevent it from propagating outside
coroutineScope { }
but my entire codebase still gets frozen...
Copy code
override suspend fun getUsers(): List<User> {
        val http = http.freeze()
        val url = "$BASE_URL$USERS_PATH".freeze()
        
        val result =
            coroutineScope {
                http.get<List<User>>(url)
            }

        return result
    }
How do you properly isolate such call? I’ve been trying random things for days
b
coroutineScope
is designed for parallel decomposition, calling single function from it is meaningless. If you want to switch thread, you need to use
withContext(<http://Dispatchers.IO|Dispatchers.IO>)
I would rewrite your example to:
Copy code
override suspend fun getUsers(): List<User> = withContext(<http://Dispatchers.IO|Dispatchers.IO>) {
        val http = http.freeze()
        val url = "$BASE_URL$USERS_PATH".freeze()

        return http.get<List<User>>(url)
    }
l
@bezrukov FYI,
<http://Dispatchers.IO|Dispatchers.IO>
is JVM-only. Here, it's about Kotlin/Native, ktor and coroutines freezing.
b
ah, see, then my answer most likely is irrelevant 🙂
r
@bezrukov yeah I don’t want to switch thread. I want to isolate a call from the current coroutine, as this call freezes the coroutine it’s ran in. I’m starting to question if that’s even possible...
l
@elizarov Is that isolation even possible? I'm also wondering for experiments, prior to open source library development exploration.
r
I mean if it’s not, the Ktor client is absolutely unusable on native...
Unless you’re ready to accept the overhead of concurrent collections and atomics absolutely everywhere in a single-threaded app, of course 🙃
e
There's curently no easy way to prevent freeze propagation. You can only detach your coroutines from your Job hiearahy (using
GlobalScope.launch
) which will allow to limit freezing to that detached part.
l
@elizarov So
GlobalScope.launch(Dispatchers.Main)
+
join()
could do?
Plus some boilerplate/extension to propagate cancellation.
I think this is working:
Copy code
suspend inline fun <R> runIsolated(noinline block: suspend CoroutineScope.() -> R): R {
    val coroutine = GlobalScope.async(Dispatchers.Main, block = block)
    try {
        return coroutine.await()
    } catch (e: CancellationException) {
        coroutine.cancel()
        throw e
    }
}
r
My real issue actually came from
CoroutineExceptionHandler
being frozen, not the coroutine suspend lambda. Not sure it should be frozen, but as long as the suspend lambda isn’t frozen (and it isn’t) I can mostly replace the CEH with a try catch.
👍 1