Difference between withContext and suspendCancellableCoroutine - kotlin

I'm new to coroutines
This is a popular example:
suspend fun findBigPrime(): BigInteger =
withContext(Dispatchers.IO) {
BigInteger.probablePrime(4096, Random()))
}
However, it could be written as well as:
suspend fun findBigPrime(): BigInteger =
suspendCancellableCoroutine {
it.resume(BigInteger.probablePrime(4096, Random()))
}
What's the real difference?

What's the real difference?
There's hardly any relationship, in fact.
suspendCancellableCoroutine {
it.resume(BigInteger.probablePrime(4096, Random()))
}
This does nothing but add useless overhead above the simple direct call
BigInteger.probablePrime(4096, Random())
If you resume the continuation while still inside the suspendCancellableCoroutine block, the coroutine doesn't suspend at all.
withContext(Dispatchers.IO) {
BigInteger.probablePrime(4096, Random()))
}
This suspends the coroutine and launches an internal coroutine on another thread. When the internal coroutine completes, it resumes the current one with the result.

Use of suspendCancellableCoroutine here is a "BIG NO".
withContext changes the context on which the block (coroutine) will run, here the Dispatcher which dispatches the coroutine to a specified thread is overridden. While the suspendCoroutine/suspendCancellableCoroutine are used for wrapping asynchronous callbacks which does not block the thread instead they run on thread of their own.
Usually work on the suspendCoroutine/suspendCancellableCoroutine is non-blocking and gets completed quite quickly and the continuation is resumed after the work has completed in a non-blocking way maybe in other thread or so.
If you put blocking code in there the concept of coroutine is lost, it will just going to block the thread it is running on.
Use of suspendCoroutine/suspendCancellableCoroutine:
// This function immediately creates and starts thread and returns
fun findBigPrimeInNewThread(after: (BigInteger) -> Unit) {
Thread {
BigInteger.probablePrime(4096, Random()).also(after)
}
}
// This just wraps the async function so that when the result is ready resume the coroutine
suspend fun findBigPrime(): BigInteger =
suspendCancellableCoroutine { cont ->
findBigPrimeInNewThread {
cont.resume(it)
}
}

Related

Difference between starting coroutine with launch or with coroutineScope

It seems I can start a coroutine using a number of mechanisms. Two of them are coroutineScope and launch but reading the Kotlin coroutine docs I am unclear what the difference is and when I would use one over the other
fun main() {
println("Main block ${Thread.currentThread().name}")
runBlocking {
coroutineScope {
println("Coroutine scope ${Thread.currentThread().name}")
}
launch {
println("Launch ${Thread.currentThread().name}")
}
}
}
I ran the above and can see that both start a new coroutine. What is the difference?
A CoroutineScope is an organisational thing - it lets you group coroutines together to enforce structured concurrency. Basically, it allows you to control the lifetime of everything within the scope (including coroutines launched within other coroutines) and handles things like propagating results and errors, and cancelling everything within a scope. More info here
All the coroutine builder functions (including launch) run inside a CoroutineScope (or on it as a receiver, if you want to look at it that way):
fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job
So the launch call in your example is running inside the current CoroutineScope, which has been provided by the runBlocking builder:
expect fun <T> runBlocking(
context: CoroutineContext = EmptyCoroutineContext,
block: suspend CoroutineScope.() -> T
): T
See how the lambda you're passing is actually a CoroutineScope.() -> T? It runs with a CoroutineScope as the receiver - that's what you're calling launch on. You can't call it from just anywhere!
So when you create a CoroutineScope in your example, you're nesting one inside this CoroutineScope that runBlocking is providing. runBlocking won't allow the rest of the code that follows it to continue (i.e. it blocks execution) until everything in its CoroutineScope has completed. Creating your own CoroutineScope inside there lets you define a subset of coroutines that you can control - you can cancel them, but if there are still other coroutines running in the runBlocking scope, it will keep blocking until they're done as well.
Like Louis says in the other answer, you're not actually creating any coroutines inside the scope you've created. The code is just running in the coroutine that runBlocking started to run the block of code you passed. Check this out:
fun main() {
println("Main")
runBlocking {
coroutineScope {
delay(1000)
println("I'm in a coroutine scope")
}
launch {
delay(500)
println("First new coroutine")
}
launch {
delay(50)
println("Second new coroutine")
}
}
}
Main
I'm in a coroutine scope
Second new coroutine
First new coroutine
Code within a single coroutine executes sequentially - so what happens is
You start in main
You enter the runBlocking block's coroutine
You create a CoroutineScope which only affects stuff you launch (etc) on it
You delay, then print a line
You hit the first launch and create a new coroutine. This starts off by delaying 500 ms, but it's running on its own now - different coroutines run concurrently
You move onto the next line, and launch the second coroutine. That runs on its own too, and immediately delays 50ms. There are 3 coroutines running now
You've finished executing the code in the block - this coroutine is done. However, there are two running coroutines that were also started on this CoroutineScope, so runBlocking waits for those to finish.
The second coroutine with the shortest delay prints first, then finishes
The first coroutine finishes last, because even though it was started earlier, they all run independently
Now everything inside the runBlocking CoroutineScope is done, it can finish and allow execution to contine in main()
If you launched a coroutine inside that scope you created, it would all work the same way - it's just you'd have fine-grained control over the stuff in there in particular, and you could specifically work with the coroutines inside that nested scope without affecting things in the outer scope. All that matters is once that inner scope has completed, it can inform the outer scope that it's all done, that kind of thing
coroutineScope doesn't start a new coroutine.
launch starts a new coroutine that can run concurrently with other coroutines. coroutineScope does not.

Kotlin unhandled coroutine exception does not crash

I expected the following program to crash but it doesn't. It just prints that an exception occurred in a thread and exits normally. Why?
import kotlinx.coroutines.*
fun main() {
val scope = CoroutineScope(Dispatchers.Default)
scope.launch {
delay(100)
throw RuntimeException("1")
}
println("sleep")
Thread.sleep(1000)
println("exit without crash")
}
playground
Since you're creating your own CoroutineScope, you have your own top-level coroutine which is not bound to the main thread. Creating a CoroutineScope manually also implies manual cleanup (otherwise you have the same pitfalls as GlobalScope and may leak coroutines or lose errors).
The main program only crashes if the main thread crashes, and the main thread doesn't crash here. If you want to have a relationship between this main thread and the launched coroutines (and propagate exceptions), you should use structured concurrency from the beginning.
For instance, using runBlocking at the top of your main() function creates a CoroutineScope (available as this) which serves as parent for all launched coroutines. It also blocks the main thread while waiting for all the child coroutines, and propagates exceptions. You don't need your own scope then:
fun main() {
runBlocking(Dispatchers.Default) { // using Dispatchers.Default to be equivalent with your code
launch { // using "this" CoroutineScope from runBlocking
delay(100)
throw RuntimeException("1")
}
println("sleep")
delay(1000) // using delay instead of Thread.sleep because we're in a coroutine now
println("exit without crash")
}
}
This way you get:
sleep
Exception in thread "main" java.lang.RuntimeException: 1
at FileKt$main$1$1.invokeSuspend (File.kt:16)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith (ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run (DispatchedTask.kt:106)

Should be used a CoroutineScope's extension function or a suspending function

I'm writing an app using coroutines (code below is greatly simplified). Recently I've watched Coroutines in Practice talk and got a little confused. Turns out I don't know when to use a CoroutineScope's extension function and when to use a suspending function.
I have a mediator (Presenter/ViewModel/Controller/etc) that implements CoroutineScope:
class UiMediator : CoroutineScope {
private val lifecycleJob: Job = Job()
override val coroutineContext = lifecycleJob + CoroutineDispatchersProvider.MAIN
// cancel parent Job somewhere
fun getChannel() {
launch {
val channel = useCase.execute()
view.show(channel)
}
}
}
Business logic (Interactor/UseCase):
class UseCase {
suspend fun execute(): RssChannel = repository.getRssChannel()
}
And a repository:
class Repository {
suspend fun getRssChannel(): RssChannel {
// `getAllChannels` is a suspending fun that uses `withContext(IO)`
val channels = localStore.getAllChannels()
if (channels.isNotEmpty()) {
return channels[0]
}
// `fetchChannel` is a suspending fun that uses `suspendCancellableCoroutine`
// `saveChannel` is a suspending fun that uses `withContext(IO)`
return remoteStore.fetchChannel()
.also { localStore.saveChannel(it) }
}
}
So I have a few questions:
Should I declare Repository#getRssChannel as a CoroutineScope's extension function (because
it spawns new suspending functions: getAllChannels,
fetchChannel, saveChannel)? How can I use it in the UseCase then?
Should I just wrap a Repository#getRssChannel into a
coroutineScope function in order to make all spawned suspending
functions to be children of the latter?
Or maybe it's already fine and I should change nothing. When to
declare a function as a CoroutineScope's extension then?
A suspending function should return once it has completed its task, it executes something, possibly taking some time while not blocking the UI, and when it's done it returns.
A CoroutineScope extension function is for a fire-and-forget scenario, you call it, it spawns a coroutine and returns immediately, while the task continues to execute.
Answer to question 1:
No, you should not declare Repository#getRssChannel as an extension function of CoroutineScope, because you only invoke suspend functions but not start (launch/ async) new jobs. As #Francesc explained extension function of CoroutineScope should only start new jobs, but cannot return immediatly result and should not be declared as suspend by itself.
Answer to question 2:
No, you should not wrap Repository#getRssChannel into a CoroutineScope. Wrapping makes only sense if you start (launch/ async) new coroutines in this method. The new jobs would be children of the current job and the outer method will only return after all parallel jobs are finished. In your case you have sequential invocations of other suspending coroutines and there is no need of a new scope.
Answer to question 3:
Yes, you can stay with your code. If you would need the functionality of UiMediator#getChannel more then once, then this method would be a candidate of an extension function for CoroutineScope.

How to suspend a coroutine at a specific point

I am at loss with the following problem.
I have the following code:
val parentJob: Job = Job()
launch(parent = parentJob) {
while (true)
{
if (!parentJob.isCompleted)
{
// I want to control suspension here
println("Resumed")
}
}
}
I would like to be able to control, somehow akin to a semaphore, when should the coroutine suspend and when to resume exactly in the commented part of the snippet
I know there's suspendCancellableCoroutine but I am unsure how to use it or if it is appropriate here
How can this be achieved or are there any tutorials about this?
It would be more helpful to think about coroutines in terms of callbacks and continuations, not threads and semaphores.
Internally, when the coroutine is suspended, the entire chain of suspend fun calls returns with a special internal object, COROUTINE_SUSPENDED. So the whole execution of a coroutine is just a function call during which a callback object is being built up. This callback is the continuation and when you call it, execution resumes from the place which returned the special COROUTINE_SUSPENDED object.
Kotlin runs the block you pass to suspendCancellableCoroutine with the continuation object as the parameter. So you should save this object and make it available to the code outside the coroutine.
Here's some code that may help your understanding. Note there's no need to create a separate parent job if you want to cancel the coroutine. You can just cancel the Job instance that launch returns and the coroutine will not resume after suspension.
import kotlin.coroutines.*
import kotlinx.coroutines.*
var continuation: Continuation<String>? = null
fun main(args: Array<String>) {
val job = GlobalScope.launch(Dispatchers.Unconfined) {
while (true) {
println(suspendHere())
}
}
continuation!!.resume("Resumed first time")
continuation!!.resume("Resumed second time")
job.cancel()
continuation!!.resume("This shouldn't print")
}
suspend fun suspendHere() = suspendCancellableCoroutine<String> {
continuation = it
}
If you still need an explicit check (because there aren't enough suspension points on the execution path), you can just use the isActive property which is available directly to the block:
while (isActive) ...

does IO in coroutines cause suspension?

Inside a coroutine I am doing a http-request with OkHttpClient. The request is done from a function that has the suspend keyword:
suspend fun doSomethingFromHttp(someParam:String): Something {
...
val response = HttpReader.get(url)
return unmarshalSomething(response)!!
}
I assume that the function can be suspended on entry since it has the suspend keyword, but will the coroutine also be suspended when doing the http-request? What about other kinds of blocking IO?
There's no automagic going on with Kotlin coroutines. If you call a blocking function like HttpReader.get(), the coroutine won't be suspended and instead the call will block. You can easily assure yourself that a given function won't cause the coroutine to suspend: if it's not a suspend function, it cannot possibly do it, whether or not it's called from a suspend function.
If you want to turn an existing blocking API into non-blocking, suspendable calls, you must submit the blocking calls to a threadpool. The easiest way to achieve it is as follows:
val response = withContext(Dispatchers.IO) { HttpReader.get(url) }
withContext is a suspend fun that will suspend the coroutine, submit the provided block to another coroutine dispatcher (here IO) and resume when that block is done and has come up with its result.
You can also easily instantiate your own ExecutorService and use it as a coroutine dispatcher:
val myPool = Executors.newCachedThreadPool().asCoroutineDispatcher()
Now you can write
val response = withContext(myPool) { HttpReader.get(url) }
This PR has example code for proper OkHttp coroutines support
https://github.com/square/okhttp/pull/4129/files
It uses the thread pools of OkHttp to do the work. The key bit of code is this generic library code.
suspend fun OkHttpClient.execute(request: Request): Response {
val call = this.newCall(request)
return call.await()
}
suspend fun Call.await(): Response {
return suspendCancellableCoroutine { cont ->
cont.invokeOnCancellation {
cancel()
}
enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
if (!cont.isCancelled) {
cont.resumeWithException(e)
}
}
override fun onResponse(call: Call, response: Response) {
if (!cont.isCancelled) {
cont.resume(response)
}
}
})
}
}
There are two types of IO libraries in JAVA world, using IO or NIO.
You can find more documentation at https://dzone.com/articles/high-concurrency-http-clients-on-the-jvm
The ones using NIO, can theoretically provide true nonblocking suspension unlike IO ones which only offload the task to a separate thread.
NIO uses some dispatcher threads in the JVM to handle the input output sockets using multiplexing (Reactor design pattern). The way it works is, we request the NIO/dispatchers to load/unload something and they return us some future reference. This code can be turned into coroutines easily.
For IO based libraries, coroutine implementation is not true non blocking. It actually blocks one of the threads just like in Java, however the general usage pattern is, to use Dispatcher.IO which is a threadpool for such blocking IO tasks.
Instead of using OkHttpClient, I would recommend using https://ktor.io/docs/client.html