Kotlin launch vs launch(Dispatchers.Default) - kotlin

I was testing out how blocking code and coroutines interact for my own understanding. Obviously, we should use non-blocking but I wanted to understand what would happen if there was some blocking code in there.
If I run the following:
fun main() {
println("Start: ${Thread.currentThread()}")
runBlocking {
println("Run Blocking : ${Thread.currentThread()}")
launch(Dispatchers.D) { // Immediately scheduled for execution
println("Inside launch: ${Thread.currentThread()}")
}
Thread.sleep(10000)
println("Going to do some other stuff now")
}
println("Out of runBlocking")
}
everything runs in the same coroutine and Inside launch: doesn't appear until the Thread.sleep has completed. I assume this is because it is blocking, so no suspending, or use of another coroutine can take place.
If I pass Dispatchers.Default to launch (I thought launch used Dispatchers.Default by default, if you didn't specify one?), the launch block runs in a separate coroutine and starts immediately.
So, why does launch(Dispatchers.Default) allow the second coroutine to start and why doesn't just launch ?

I thought launch used Dispatchers.Default by default, if you didn't specify one
This is where you're mistaken. Here is the documentation:
The coroutine context is inherited from a CoroutineScope. Additional context elements can be specified with context argument. If the context does not have any dispatcher nor any other ContinuationInterceptor, then Dispatchers.Default is used.
This means that if you don't pass a dispatcher to launch, it will default to the dispatcher present in the context of the CoroutineScope in which you started that launch. If the coroutine context doesn't have a dispatcher, then launch will use Dispatchers.Default.
So in your case, a parameterless launch inherits the context and thus the dispatcher provided by runBlocking, which is a single-threaded dispatcher running on the thread that it blocks (the main thread here).
why does launch(Dispatchers.Default) allow the second coroutine to start and why doesn't just launch ?
This is because, as we've just seen, these 2 cases don't involve the same threads. With launch(Dispatchers.Default), the launched coroutine runs on a separate thread pool and can run in parallel of the main one. However, with the parameterless launch, the second coroutine runs on the same thread as the rest of the code, because it runs in the single-threaded dispatcher of runBlocking, which is backed by the main thread.
The last piece of the puzzle is that Thread.sleep is blocking (as you said), unlike delay which is the suspend equivalent from the coroutines library. When there is only one thread involved (in the case of the parameterless launch), running the last Thread.sleep blocks the main thread and never gives a chance to the other coroutine to run. You would need something to suspend execution, for instance delay().

Related

diff between executing coroutine with and without suspend-ed IO methods

Let's assume I have the code:
// Android ViewModel
class MyVM(val dao: MyDao) : ViewModel() {
fun onButtonInsert() {
// point 1
viewModelScope.launch {
// point 2
dao.insert_SuspendFun(MyData(id=1, "hello, it works"))
// point 3
dao.insert_NOT_SuspendFun(MyData(id=2, "hello, it fails"))
// point 4
}
}
}
// Room DAO
#Dao
interface MyDao {
#Insert
suspend fun insert_SuspendFun(md: MyData)
#Insert
fun insert_NOT_SuspendFun(md: MyData)
}
Now when fun onButtonInsert runs then:
1st line works:
dao.insert_SuspendFun(MyData(id=1, "hello, it works"))
but 2nd line:
dao.insert_NOT_SuspendFun(MyData(id=2, "hello, it fails"))
fails with the exception:
java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
"The only difference" is that fun insert_NOT_SuspendFun has no suspend keyword.
Both methods run in the same coroutine.
Can someone explain what happens under the hood?
How are threads working in this coroutine?
Why does 1st call use non-UI thread but 2nd uses UI thread?
Thanks! ;]
Room generates the code for the implementation of your DAO, so the behaviors are based on their design decisions.
The non-suspend function runs on whatever thread you call it from, which is the only possible way a non-suspend function can run. Since Google doesn’t want people making janky apps that do IO on the main thread, they decided to make the generated implementation under the hood check the thread and throw an exception if it’s the main thread.
The suspend function implementation that Room generates suspends while the IO is done on a background thread. This follows the Kotlin coroutines convention that a suspend function should never block the thread it is called from.
Any suspend function can internally suspend the calling code until they finish and resume. Other than by calling other suspend functions, this is most commonly done using withContext, coroutineScope, suspendCoroutine, or suspendCancellableCoroutine. Those last two are low level and allow the coroutine to be suspended and then resumed at will from any other thread.
I have not checked the generated source, but I think it’s likely Room generates the suspend function implementation by using suspendCancellableCoroutine and runs the IO work using an internal thread pool before resuming the coroutine.
An alternative way they could have done it is by using withContext(Dispatchers.IO) and then done the blocking IO work within it. This is possibly the most common and easiest way to do blocking work in a coroutine. It uses the IO Dispatcher (a thread pool provided by Kotlin coroutines).
As for why your coroutine runs on the main thread, it’s because viewModelScope’s attached dispatcher uses the main thread. You can override it by passing a specific dispatcher to your launch call, but this is not often done if you’re following conventions. Android is full of functions that must be called from the main thread, so it usually looks cleanest to keep coroutines on the main dispatcher and only do the background work using suspend functions and withContext blocks. Exception would be a coroutine that runs blocking code and doesn’t need to do anything on the main thread.
You're trying to run a long-running synchronize task in side main thread as it does not mention suspend keyword to make it async.
You can use both methods in the IO thread instead main;
viewmodelscope.launch(dispatchers.io)

Why is coroutine being called after the line below it? Even where there is no delay given to coroutine

In this example,
Why is coroutine being called after the line below it?
Even where there is no delay given to coroutine.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
GlobalScope.launch {
println("Hello from coroutine")
}
println("Hello from outside")
Output:
I/System.out: Hello from outside
I/System.out: Hello from courtine
Try Dispatchers.Main.immediate
Returns dispatcher that executes coroutines immediately when it is
already in the right context (e.g. current looper is the same as this
handler's looper) without an additional re-dispatch.
eg:
GlobalScope.launch(Dispatchers.Main.immediate) {
println("Hello from coroutine")
}
In this example, Why is coroutine being called after the line below
it?
Even where there is no delay given to coroutine.
If you see how the launch function works, it has a parameter start which defaults to CoroutineStart.DEFAULT
then the coroutine code is dispatched for execution later, while the
code that invoked the coroutine builder continues execution.
Since the GlobalScope is used, its CoroutineContext defaults to EmptyCoroutineContext, which doesn't have any of CoroutineContext.Element. So once a coroutine is fired using it, the coroutine will get the Dispatchers.Default as a CoroutineDispatcher which is backed by a shared pool of threads, meaning that the coroutine code will be executed by a different thread (you can add ${Thread.currentThread().name} to each println to see what thread the code is executed by), hence the order, it's not guaranteed that either one will happen first, it's "random" whichever thread gets there first.
If you were to run this code in a normal Kotlin JVM project through the main function, the coroutine code wouldn't be guaranteed to run at all, because the main function could've been finished by the time the coroutine gets to be executed (on a separate thread).

Kotlin Coroutine Execution Order Same Thread

I have come across some code that creates a CoroutineScope with a single thread context
val serialThreadContext = newSingleThreadContext("mysinglethreadcontext")
fun myScope(): CoroutineScope = CoroutineScope(serialThreadContext)
Then elsewhere in the code base, coroutines are launched in this scope...
myScope().launch {
someOtherMethod()
...
}
From what I understand, all of these created coroutines will be scheduled on the same thread, but I can't find any specific documentation on order of execution or suspension.
This raises the following questions in my mind:
if someOtherMethod doesn't contain any suspend functions, can this coroutine still be suspended by the thread?
is there any guaranteed order of execution when the thread pulls the coroutine from the scheduler?
Coroutines can only be suspended at suspend functions. Once someOtherMethod is invoked, if it is not a suspend function, there is no way to avoid waiting for that function to return before the thread can be freed up by suspending. Note that a function being marked suspend still doesn't mean it will necessarily suspend the coroutine calling it. If it doesn't internally do suspending work on some other dispatcher, it will still be occupying your calling thread.
There is no guarantee of execution order. You should not rely on a single-threaded dispatcher to try to queue work in order. See my answer here for an example of how you could create a work queue with a Channel.

What is the CoroutineScope of runBlocking in Kotlin Coroutines?

What is the CoroutineScope of
runBlocking {
// some code here
}
in Kotlin Coroutines?
Is it local to the class where it is called and the Coroutine is garbage collected when the Class is garbage collected or will it cause memory leak if its still running?
Unlike launch or async, runBlocking is a very special coroutine builder because it is supposed to be used at the top-level. It is therefore not run inside a scope (there is no CoroutineScope as receiver as you can see). It is rather meant to be a "root" of structured concurrency.
runBlocking actually provides you with a scope, so you can start "child" coroutines inside it. What really matters is that runBlocking will wait for all coroutines that you start in that scope (child coroutines) to finish before returning.
Cancelling runBlocking?
You cannot really cancel the runBlocking coroutine itself externally if it hangs (like you would do by cancelling the scope of launch or async), because it's not an async task - it's blocking a thread. You could interrupt the thread running it, or you could also keep track of nested coroutines and cancel them explicitly to make runBlocking finish.
A runBlocking call will only return once all child coroutines have completed (or were cancelled). Throwing an exception (from inside) also cancels all child coroutines and makes runBlocking rethrow that exception. So it's also a way to "cancel" runBlocking from inside.
Is it local to the class where it is called and the Coroutine is garbage collected when the Class is garbage collected or will it cause memory leak if its still running?
You could see runBlocking as any blocking function, like Thread.sleep, there is no more magic. Just like Thread.sleep, runBlocking can be called from any function, even top-level, in which case there wouldn't be any class instance involved.
Let's assume a method in a class calls runBlocking, and whatever is inside runBlocking hangs for a long time (like a long sleep). Then, whoever is calling this method holds a reference to the instance until the method returns or fails, so the instance won't be garbage collected anyway. In this case the caller will hang, blocking whatever thread it's running in - that's where leaks can happen.
Examples of problems with runBlocking
Spring controller
If you use runblocking in the methods of a Spring MVC controller, Spring could be creating threads for each of those, and if anything hangs inside runBlocking you kinda leak the thread. Instead, you can use Spring WebFlux which allows you to use suspend functions directly in your Spring controllers (and add a request timeout to automatically cancel the hanging stuff).
Callbacks
Using runBlocking inside callbacks might be dangerous too. The hypothetical callback-based API you're using might be using a thread pool to call you back, and if runBlocking hangs it could be blocking that thread pool.
If that API supports backpressure, it might be OK to block though. The API could also support this in a non-blocking way (like JDK11's websocket listener which let you return a CompletableStage from the callbacks), in which case you should should build a Future instead of blocking.
If the API doesn't support backpressure, you might have to resort to creating a custom CoroutineScope and launch-ing coroutines in it to handle the callbacks (instead of runBlocking). You'll have to cancel that scope manually when you're not using the callback based API anymore.

why coroutines 1st run on caller thread but after first suspension point it runs on DefaultExecutor in “Dispatchers.Unconfined”?

Below code prints
import kotlinx.coroutines.*
fun main() = runBlocking<Unit> {
launch(Dispatchers.Unconfined) {
// not confined -- will work with main thread
println("thread ${Thread.currentThread().name}")
delay(500)
println("thread ${Thread.currentThread().name}")
}
}
1st run on caller thread but after first suspension point it runs on DefaultExecutor in “Dispatchers.Unconfined”
thread main
thread kotlinx.coroutines.DefaultExecutor
Read the description of Dispatchers.Unconfined, it explains exactly this behavior:
A coroutine dispatcher that is not confined to any specific thread. It executes initial continuation of the coroutine immediately in the current call-frame and lets the coroutine resume in whatever thread that is used by the corresponding suspending function, without mandating any specific threading policy. Note: use with extreme caution, not for general code.
The Unconfined dispatcher has no thread associated with it. In almost all circumstances the coroutine resumes on the thread where the users calls continuation.resume().
In the specific case you're showing, you call the standard function delay so Kotlin must internally handle both its suspension and resumption. To make this happen, it maintains a scheduled thread pool to which it submits the resumption task, scheduled with the delay you specify.
The Unconfined dispatcher is useful only in specialized scenarios, where you write your own infrastructure and want to be in control of the thread where the coroutine resumes. Basically, it short-circuits the entire dispatcher mechanism.