Christopher Porto
08/18/2022, 7:37 AMsuspend fun test() {
val response = bar()
println("I want this to be on same thread as it was before doing bar call")
}
suspend fun bar() {
val response = someOtherCallThatSwitchesThread()
return response
}
I figured calling suspendCoroutine inside bar then launching new coroutine that resumes the continuation with the response would work and while it does change the thread, it doesnt go back to original.simon.vergauwen
08/18/2022, 7:42 AMsuspendCoroutine takes care of switching back to the original thread.
I'm not sure what someOtherCallThatSwitchesThread is doing but you probably want,
suspend fun bar() =
suspendCoroutine {
val response = someOtherCallThatSwitchesThread()
cont.resume(response)
}
Or in case someOtherCallThatSwitchesThread() is some Java SDK library which does blocking you might want to use:
runInterruptible {
someOtherCallThatSwitchesThread()
}Christopher Porto
08/18/2022, 7:59 AMsomeOtherCallThatSwitchesThread is suspendable function in networking library (which launches coroutine in its own thread, or something along those lines)
So that being said since someOtherCallThatSwitchesThread is suspendable I need to call it within a coroutine. I created a scope and did the calls in there, not sure if thats right
suspend fun bar() = suspendCoroutine {
scope.launch {
val response = someOtherCallThatSwitchesThread()
cont.resume(response)
}
}simon.vergauwen
08/18/2022, 8:01 AMsomeOtherCallThatSwitchesThread behaving incorrectly. The bar definition should work, but it's definitely a patch rather than a fix for this issue.simon.vergauwen
08/18/2022, 8:02 AMwithContext(OtherContext) {
someOtherCallThatSwitchesThread()
}
This should also work, as long as OtherContext is not the same as the context your code is running on.Christopher Porto
08/18/2022, 9:05 AMNick Allen
08/18/2022, 5:11 PMbar? What you are asking for is only possible if the dispatcher is single threaded. You never specify if that's the case.Nick Allen
08/18/2022, 5:22 PMsuspendCouroutine is for suspending while waiting for callback APIs, launching inside is just confusing and odd. If you want to force dispatcher to redispatch, you could try just calling yield.Christopher Porto
08/19/2022, 1:14 AMbar is single threaded which is why I want continuation to stay on that thread since continuation logic isnt thread safe.
someOtherCallThatSwitchesThread is suspendable function from third party library that later on launches coroutine with multi threaded dispatcher to invoke its logic which I assume the continuation then gets passed and executes on it (which I want to avoid)
What I have works now but I agree launch inside suspendCoroutine is a bit odd. One key thing is I require the response from someOtherCallThatSwitchesThread to resume continuation, not sure if yield is the solution here, kinda unfamiliarNick Allen
08/19/2022, 1:31 AMsomeOtherCallThatSwitchesThread. The problem you are running into shouldn't be possible with the usual coroutine APIs and one or the other is breaking very fundamental assurances of coroutines if you are ending up not on the expected dispatcher. It's generally difficult to resume continuations on the wrong thread because they are almost always wrapped in a continuation that delegates to the CoroutineInterceptor (dispatcher). If someOtherCallThatSwitchesThread is broken, I would avoid all coroutine APIs of that library if not the entire library and log a bug so they can fix their issue.simon.vergauwen
08/19/2022, 7:22 AMIt's generally difficult to resume continuations on the wrong thread because they are almost always wrapped in a continuation that delegates to the(dispatcher)CoroutineInterceptor
suspend fun main() starts a coroutine on the main thread, but with EmptyCoroutineContext then you will also run into the scenario that intercepted() doesn't bring you back to the original thread and thus context preservation breaks.simon.vergauwen
08/19/2022, 7:22 AMsingleThreadContext should still always bring you back to the proper context if it's properly implemented. To me it looks like someOtherCallThatSwitchesThread is broken, and is not preserving the context correctly.Christopher Porto
08/21/2022, 2:33 PMbut withThis was the case 😅 Took a look at our implementation of our custom continuation and it didnt keep context and instead usedthen you will also run into the scenario thatEmptyCoroutineContextdoesn't bring you back to the original thread and thus context preservation breaks.intercepted()
EmptyCoroutineContext
Thanks you two