Hello, I was wondering if its possible to ensure c...
# coroutines
c
Hello, I was wondering if its possible to ensure continuation is ran on same thread from call site, for e.g
Copy code
suspend 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.
s
suspendCoroutine
takes care of switching back to the original thread. I'm not sure what
someOtherCallThatSwitchesThread
is doing but you probably want,
Copy code
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:
Copy code
runInterruptible {
  someOtherCallThatSwitchesThread()
}
c
Thats what I thought but after checking thread before and after the call, while it did change thread, it wasnt to original.
someOtherCallThatSwitchesThread
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
Copy code
suspend fun bar() = suspendCoroutine {
    scope.launch {
        val response = someOtherCallThatSwitchesThread()
        cont.resume(response)
    }
}
s
This means
someOtherCallThatSwitchesThread
behaving incorrectly. The
bar
definition should work, but it's definitely a patch rather than a fix for this issue.
Copy code
withContext(OtherContext) {
  someOtherCallThatSwitchesThread()
}
This should also work, as long as
OtherContext
is not the same as the context your code is running on.
c
Okay think its working, thanks 😛
n
What dispatcher are you using when you call
bar
? What you are asking for is only possible if the dispatcher is single threaded. You never specify if that's the case.
And
suspendCouroutine
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
.
c
@Nick Allen Right my bad should have clarified, the dispatcher used prior to
bar
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 unfamiliar
n
How is your single threaded dispatcher defined? Something is either broken with the dispatcher or broken with
someOtherCallThatSwitchesThread
. 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.
s
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)
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.
A proper
singleThreadContext
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.
c
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.
This was the case 😅 Took a look at our implementation of our custom continuation and it didnt keep context and instead used
EmptyCoroutineContext
Thanks you two