How exactly is a coroutine suspended? - kotlin

From the kotlin docs
A coroutine is an instance of suspendable computation.
It may suspend its execution in one thread and resume in another one.
delay is a special suspending function.
It suspends the coroutine for a specific time.
Suspending a coroutine does not block the underlying thread, but allows other coroutines to run and use the underlying thread for their code.
When a coroutine is suspended the thread which was running it is free to execute some other coroutine. For example when you use delay() or callSomeAPI() they are done asynchronously.
I come from javascript world. There functions like setTimeout() or fetch() were executed outside of javascript callstack and inside the browser environment.
But in our kotlin case who exactly is executing those methods? Shouldn't there be a thread which manages stuff? So do we start a new thread to take care of the async code in coroutines?

It really depends on what is the reason to suspend. For example, if we suspend to wait for some IO, then it is probable that underneath another thread is used to block on this IO. Some IO are event-based, so OS notifies our application that the coroutine could be resumed. But in many cases we suspend waiting for another coroutine, for example we wait for its completion or we wait on reading from/writing to a Channel (suspendable queue). Then, there is no need for additional thread - other coroutines resume our coroutine. delay() is another example. I'm not sure how does it work internally, it definitely depends on the target platform, but I don't suspect it to do busy-waiting ;-) I guess there is some kind of a timer event provided by the OS.
So once again: it depends.

import kotlinx.coroutines.*
import java.util.concurrent.Executors
suspend fun main(args: Array<String>) {
coroutineScope {
val start = System.currentTimeMillis()
launch(Executors.newSingleThreadExecutor().asCoroutineDispatcher()) { // #1
launch {
println("1 --> : " + (System.currentTimeMillis() - start))
Thread.sleep(1000L)
println("1 <--: " + (System.currentTimeMillis() - start))
}
launch { // #3
println("2 --> : " + (System.currentTimeMillis() - start))
delay(1000L)
println("2 <--: " + (System.currentTimeMillis() - start))
}
}
}
coroutineScope {
val start = System.currentTimeMillis()
launch(Executors.newScheduledThreadPool(8).asCoroutineDispatcher()) { // #2
launch {
println("1` --> : " + (System.currentTimeMillis() - start))
Thread.sleep(1000L)
println("1` <--: " + (System.currentTimeMillis() - start))
}
launch { // #4
println("2` --> : " + (System.currentTimeMillis() - start))
delay(1000L)
println("2` <--: " + (System.currentTimeMillis() - start))
}
}
}
}
sample output:
1 --> : 56
1 <--: 1063
2 --> : 1063
2 <--: 2086
1` --> : 7
2` --> : 8
1` <--: 1012
2` <--: 1012
watch out for #1 and #2 lines, in #1 we launch coroutine with a single thread executor (something similar to Dispatchers.Main), so the coroutine are back-ended with a single thread, when we call a blocking function Thread.sleep, there's no change to start coroutine in #3, so #3 gets invoked after the entire launch,
as for #2, the same thing happen, but there are lots of remaining thread to let #4 gets launched
so
But in our kotlin case who exactly is executing those methods? Shouldn't there be a thread which manages stuff? So do we start a new thread to take care of the async code in coroutines?
not a thread, but a CoroutineContext, e.s.p CoroutineIntercepter which may defined in the parameter of CoroutineBuilder(lets say ,launch or something) or the parent scope,
all stuffs are not os/platform specific but user-land scheduler controlled by programmer

Related

Kotlin coroutine delay waits for couroutine completion/fire and forget

I have the following code.
fun main(args: Array<String>) {
runBlocking {
while (true) {
launch {
println("Taking measurement")
val begin = System.currentTimeMillis()
while (System.currentTimeMillis() - begin < 20000) {
val test = 5 + 5
}
println("Took measurement")
}
println("After launch")
delay(1000)
println("After delay")
}
}
}
I would expect the output of that to be twice as many "After launch"s and "After delay"s in the first 20 seconds. SO the output should look something like this:
After launch
Taking measurement
After delay
After launch
Taking measurement
After delay
After launch
Taking measurement
After delay
After launch
Taking measurement
After delay
After launch
Taking measurement
... and then after 20s the first took measurements
But rather it looks like this and I can not wrap my head around to why it is not working.
After launch
Taking measurement
Took measurement
After delay
After launch
Taking measurement
What I basically want to achieve is a fire and forget. I want to start the code in launch and then 1 second after that I want to start it again.... The result of that code will be saved by that code, so I do not need any data back.
Any tips on why this is not working?
This is because the default dispatcher of runBlocking is a single-threaded dispatcher that uses the same thread that runBlocking was called on. Since it is single-threaded, it cannot run the blocking portions of your coroutine concurrently. If you use runBlocking(Dispatchers.Default) or launch(Dispatchers.Default) to avoid running in the single-threaded dispatcher, it will behave as you had expected.
However, the only reason you ran into this surprise in the first place is that you are trying to do blocking work directly in a coroutine, which you're not supposed to do. You should not do blocking work in a coroutine without wrapping it in withContext or a suspend function that uses withContext. If you replace your blocking loop in the coroutine with a delay(1000) suspend function call to simulate doing work in a non-blocking way, it will behave as you had expected.
When you do blocking work in a coroutine, you should either wrap it in a withContext call like this:
withContext(Dispatchers.Default) {
while (System.currentTimeMillis() - begin < 20000) {
val test = 5 + 5
}
}
or break it out into a suspend function that uses withContext to perform the blocking work on an appropriate dispatcher:
suspend fun doLongCalculation() = withContext(Dispatchers.Default) {
val begin = System.currentTimeMillis()
while (System.currentTimeMillis() - begin < 20000) {
val test = 5 + 5
}
}
This is why replacing your blocking work with a delay suspend function call is an adequate simulation of doing long-running work. delay and the doLongCalculation() above are both proper suspend functions that do not block their caller or expect their caller to be using a specific dispatcher to function correctly.

Kotlin coroutines - delay, how does it work?

I am quite used to using RX to handle concurrency, but, in my current job, we have a mix of AsyncTask, Executors + Handlers, Threads and some LiveData thrown in. Now we are thinking about moving towards using Kotlin Coroutines (and in fact have started using it in certain places in the codebase).
Therefore, I need to start wrapping my head around Coroutines, ideally drawing from my existing knowledge of concurrency tools to speed the process up.
I have tried following the Google codelab for them and whilst it's giving me a bit of understanding it's also raising lots of unanswered questions so I've tried getting my hands dirty by writing some code, debugging and looking at log outputs.
As I understand it, a coroutine is composed of 2 main building blocks; suspend functions which are where you do your work and coroutine contexts which is where you execute suspend functions such that you can have a handle on what dispatchers the coroutines will run on.
Here I have some code below, that behaves as I would expect. I have set up a coroutine context using Dispatchers.Main. So, as expected, when I launch the coroutine getResources it ends up blocking the UI thread for 5 seconds due to the Thread.sleep(5000):
private const val TAG = "Coroutines"
class MainActivity : AppCompatActivity(), CoroutineScope {
override val coroutineContext: CoroutineContext = Job() + Dispatchers.Main
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
log("onCreate", "launching coroutine")
launch {
val resource = getResource()
log("onCreate", "resource fetched: $resource")
findViewById<TextView>(R.id.textView).text = resource.toString()
}
log("onCreate", "coroutine launched")
}
private suspend fun getResource() : Int {
log("getResource", "about to sleep for 5000ms")
Thread.sleep(5000)
log("getResource", "finished fetching resource")
return 1
}
private fun log(methodName: String, toLog: String) {
Log.d(TAG,"$methodName: $toLog: ${Thread.currentThread().name}")
}
}
When I run this code, I see the following logs:
2020-05-28 11:42:44.364 9819-9819/? D/Coroutines: onCreate: launching coroutine: main
2020-05-28 11:42:44.376 9819-9819/? D/Coroutines: onCreate: coroutine launched: main
2020-05-28 11:42:44.469 9819-9819/? D/Coroutines: getResource: about to sleep for 5000ms: main
2020-05-28 11:42:49.471 9819-9819/com.example.coroutines D/Coroutines: getResource: finished fetching resource: main
2020-05-28 11:42:49.472 9819-9819/com.example.coroutines D/Coroutines: onCreate: resource fetched: 1: main
As you can see, all the logs originated from the main thread, and there is a 5 second gap between the log before and after the Thread.sleep(5000). During that 5 second gap, the UI thread is blocked, I can confirm this by just looking at the emulator; it doens't render any UI because onCreate is blocked.
Now, if I update the getResources function to use the suspend fun delay(5000) instead of using Thread.sleep(5000) like so:
private suspend fun getResource() : Int {
log("getResource", "about to sleep for 5000ms")
delay(5000)
log("getResource", "finished fetching resource")
return 1
}
Then what I end up seeing confuses me. I understand delay isn't the same as Thread.sleep, but because I am running it within the coroutine context which is backed by Dispatchers.Main, I expected to see the same result as using Thread.sleep.
Instead, what I see is the UI thread is not blocked while the 5 second delay is happening, and the logs look like:
2020-05-28 11:54:19.099 10038-10038/com.example.coroutines D/Coroutines: onCreate: launching coroutine: main
2020-05-28 11:54:19.111 10038-10038/com.example.coroutines D/Coroutines: onCreate: coroutine launched: main
2020-05-28 11:54:19.152 10038-10038/com.example.coroutines D/Coroutines: getResource: about to sleep for 5000ms: main
2020-05-28 11:54:24.167 10038-10038/com.example.coroutines D/Coroutines: getResource: finished fetching resource: main
2020-05-28 11:54:24.168 10038-10038/com.example.coroutines D/Coroutines: onCreate: resource fetched: 1: main
I can see the UI thread is not blocked in this case as the UI renders whilst the delay is taking place and then the text view is updated after 5 seconds.
So, my question is, how does delay, in this case, not block the UI thread (even though the logs in my suspend function still indicate that the function is running on the main thread...)
Think of suspend functions as a way to use a function that takes a callback, but doesn't require you to to pass that callback into it. Instead, the callback code is everything under the suspend function call.
This code:
lifecycleScope.launch {
myTextView.text = "Starting"
delay(1000L)
myTextView.text = "Processing"
delay(2000L)
myTextView.text = "Done"
}
Is somewhat like:
myTextView.text = "Starting"
handler.postDelayed(1000L) {
myTextView.text = "Processing"
handler.postDelayed(2000L) {
myTextView.text = "Done"
}
}
Suspend functions should never be expected to block. If they do, they have been composed incorrectly. Any blocking code in a suspend function should be wrapped in something that backgrounds it, like withContext or suspendCancellableCoroutine (which is lower level because it works directly with the coroutine continuation).
If you try to write a suspend function like this:
suspend fun myDelay(length: Long) {
Thread.sleep(length)
}
you will get a compiler warning for "Inappropriate blocking method call". If you push it to a background dispatcher, you won't get the warning:
suspend fun myDelay(length: Long) = withContext(Dispatchers.IO) {
Thread.sleep(length)
}
If you try to send it to Dispatchers.Main, you will get the warning again, because the compiler considers any blocking code on the Main thread to be incorrect.
This should give you and idea of how a suspend function should operate, but keep in mind the compiler cannot always recognize a method call as blocking.
The best way to connect your existing intuition with the world of coroutines is to make this mental mapping: whereas in the classical world, the OS schedules threads to CPU cores (preemptively suspending them as needed), a dispatcher schedules coroutines to threads. Coroutines can't be preemptively suspended, this is where the cooperative nature of coroutine concurrency comes in.
With that in mind:
because I am running it within the coroutine context which is backed by Dispatchers.Main, I expected to see the same result as using Thread.sleep.
delay(delayTime) simply suspends the coroutine and schedules its resumption delayTime later. Therefore you should expect to see a very different result than with Thread.sleep, which never suspends a coroutine and keeps occupying its thread, a situation comparable to one where Thread.sleep() wouldn't allow the CPU core to run other stuff, but would busy-wait.

Why Kotlin coroutines run in the same thread sequentially?

I thought that calling a "suspend" function from coroutine context using launch makes the call asynchronous. But in the example below I see that 2 invocations of placeOrder method are not running in the same thread one after another.
What is my mistake?
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import java.io.File
fun main() = runBlocking {
t("1")
launch {
t("2")
placeOrder("C:\\Users")
t("3")
}
launch {
t("12")
placeOrder("C:\\Program Files")
t("13")
}
t("4")
}
fun t(s: String) {
val currentThread = Thread.currentThread()
println(s + ": " + currentThread.name + " " + currentThread.id)
}
suspend fun placeOrder(d:String): String {
t("placeOrder $d")
val user = createUser(d) // asynchronous call to user service
val order = createOrder(user) // asynchronous call to order service
t("placeOrder $d finished")
return order
}
suspend fun createUser(d:String): String {
t("createUser $d")
val toString = File(d).walk().map {
it.length()
}.sum().toString()
t("createUser $d finished")
return toString
}
suspend fun createOrder(user: String): String {
t("createOrder $user")
val toString = File("C:\\User").walk().map {
it.length()
}.sum().toString()
t("createOrder $user finished")
return toString
}
Output:
1: main 1
4: main 1
2: main 1
placeOrder C:\Users: main 1
createUser C:\Users: main 1
createUser C:\Users finished: main 1
createOrder 1094020270277: main 1
createOrder 1094020270277 finished: main 1
placeOrder C:\Users finished: main 1
3: main 1
12: main 1
placeOrder C:\Program Files: main 1
createUser C:\Program Files: main 1
createUser C:\Program Files finished: main 1
createOrder 5651227104: main 1
createOrder 5651227104 finished: main 1
placeOrder C:\Program Files finished: main 1
13: main 1
Instead of writing suspendable IO, you wrote blocking IO:
File(d).walk().map {
it.length()
}
Your functions never actually suspend and instead they block the single thread associated with their runBlocking dispatcher.
You gave your coroutines no opportunity to execute concurrently.
If you applied withContext(IO) { ... } around the above code, you'd get concurrency, but of the plain-old Java type, several threads being blocked in IO operations together.
The reason for this behavior is twofold:
All your coroutines are executed in the runBlocking scope, which is a single-threaded event loop. So this means only a single thread is ever used unless a different context is specified. (launch(Dispatchers.IO) as an example)
Even then it would be possible for the coroutines to interleave, except your coroutines do call suspending functions which actually have to suspend. This means it is effectively a normal sequential function call. If your functions included a yield() or delay(..) call you would see the coroutines interleave in execution.
launch function signature:
fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job (source)
As per the official Kotlin documentation link:
When launch { ... } is used without parameters, it inherits the context (and
thus dispatcher) from the CoroutineScope it is being launched from.
In your case, it inherits the context of the main runBlocking coroutine which runs in the main thread.
As coroutine context includes a coroutine dispatcher that determines what thread or threads the corresponding coroutine uses for its execution, so you can provide a different CoroutineContext with your launch coroutine builder. For Example:
fun main() = runBlocking {
t("1")
launch(Dispatchers.Default) {
t("2")
placeOrder("C:\\Users")
t("3")
}
launch(Dispatchers.Default) {
t("12")
placeOrder("C:\\Program Files")
t("13")
}
t("4")
}
Regarding suspend functions, a suspending function is just a regular Kotlin function with an additional suspend modifier which indicates that the function can suspend the execution of a coroutine. It doesn't make the call asynchronous by default.
You can execute your function code with custom dispatcher(e.g. IO dispatcher) using Kotlin's withContext() function as below:
suspend fun get(url: String) = withContext(Dispatchers.IO){/* Code for N/W logic */}
This will execute the body of the function in separate Thread than the calling coroutine context.
Here is 3-part series blog explaining usage of coroutines in Android apps :
https://medium.com/androiddevelopers/coroutines-on-android-part-i-getting-the-background-3e0e54d20bb
replace launch with async
read this
Essentially the code above is running synchronously even without runBlocking!
Since all the coroutines are launched into the main running on a single thread, ultimately you can use IO dispatcher which uses multiple threads.
Also, note that multiple coroutines can run on a single thread but they are never executed in parallel, they may appear as running in parallel because of thread switching from one coroutine to another when a new coroutine is launched or suspended.

Does Kotlin delay use a dispatcher internally to unblock the caller thread?

This is some test code I am using to learn kotlin coroutines. The code works as expected and takes about 1 Second to print the sum, but now If I replace delay(1000) by a blocking call like a network request, then the code takes about 10 seconds to print the sum ( each call takes around 1 second), but if I wrap the network call in a withContext and use the IO dispatcher it takes 1 second to print the sum, because it is run on a different thread. Does the delay function use some kind of a dispatcher to unblock the thread?
suspend fun asyncDoubleFn(num: Int): Int {
delay(1000)
return num * 2
}
fun main() = runBlocking {
launch {
val tt = measureTimeMillis {
val results = mutableListOf<Deferred<Int>>()
for (num in 0..10) {
val result = async { asyncDoubleFn(num + 1) }
results.add(result)
}
val sum = results.map { it.await() }.reduce { acc, i -> acc + i }
println("[SUM]: $sum")
}
println("[TT]: $tt")
}
launch {
println("Another coroutine")
}
println("Main Code")
}
Does the delay function use some kind of a dispatcher to unblock the thread?
Not just delay. All suspendable functions interact with the dispatcher.
The question you should ask is: "Which dispatcher is in charge here?"
And the answer is: runBlocking installs its own dispatcher that dispatches to the thread it is called on. Both launch and async in your code inherit it.
With this in mind:
If I replace delay(1000) by a blocking call like a network request, then the code takes about 10 seconds to print the sum (each call takes around 1 second)
Each blocking call will hold on to the single thread of the dispatcher. It won't be able to do any other work while being blocked.
but if I wrap the network call in a withContext and use the IO dispatcher it takes 1 second to print the sum, because it is run on a different thread
Yes, this changes the dispatcher, problem solved.
So, what does delay do? It suspends the current coroutine (the one that async launched) and returns the control to the dispatcher, which can now go on to resume your loop and start the next coroutine.

What does the suspend function mean in a Kotlin Coroutine?

I'm reading Kotlin Coroutine and know that it is based on suspend function. But what does suspend mean?
Coroutine or function gets suspended?
From https://kotlinlang.org/docs/reference/coroutines.html
Basically, coroutines are computations that can be suspended without blocking a thread
I heard people often say "suspend function". But I think it is the coroutine who gets suspended because it is waiting for the function to finished? "suspend" usually means "cease operation", in this case the coroutine is idle.
Should we say the coroutine is suspended ?
Which coroutine gets suspended?
From https://kotlinlang.org/docs/reference/coroutines.html
To continue the analogy, await() can be a suspending function (hence also callable from within an async {} block) that suspends a coroutine until some computation is done and returns its result:
async { // Here I call it the outer async coroutine
...
// Here I call computation the inner coroutine
val result = computation.await()
...
}
It says "that suspends a coroutine until some computation is done", but coroutine is like a lightweight thread. So if the coroutine is suspended, how can the computation is done ?
We see await is called on computation, so it might be async that returns Deferred, which means it can start another coroutine
fun computation(): Deferred<Boolean> {
return async {
true
}
}
The quote say that suspends a coroutine. Does it mean suspend the outer async coroutine, or suspend the inner computation coroutine?
Does suspend mean that while outer async coroutine is waiting (await) for the inner computation coroutine to finish, it (the outer async coroutine) idles (hence the name suspend) and returns thread to the thread pool, and when the child computation coroutine finishes, it (the outer async coroutine) wakes up, takes another thread from the pool and continues?
The reason I mention the thread is because of https://kotlinlang.org/docs/tutorials/coroutines-basic-jvm.html
The thread is returned to the pool while the coroutine is waiting, and when the waiting is done, the coroutine resumes on a free thread in the pool
Suspending functions are at the center of everything coroutines.
A suspending function is simply a function that can be paused and resumed at a later time. They can execute a long running operation and wait for it to complete without blocking.
The syntax of a suspending function is similar to that of a regular function except for the addition of the suspend keyword. It can take a parameter and have a return type. However, suspending functions can only be invoked by another suspending function or within a coroutine.
suspend fun backgroundTask(param: Int): Int {
// long running operation
}
Under the hood, suspend functions are converted by the compiler to another function without the suspend keyword, that takes an addition parameter of type Continuation<T>. The function above for example, will be converted by the compiler to this:
fun backgroundTask(param: Int, callback: Continuation<Int>): Int {
// long running operation
}
Continuation<T> is an interface that contains two functions that are invoked to resume the coroutine with a return value or with an exception if an error had occurred while the function was suspended.
interface Continuation<in T> {
val context: CoroutineContext
fun resume(value: T)
fun resumeWithException(exception: Throwable)
}
But what does suspend mean?
Functions marked with the suspend keyword are transformed at compile time to be made asynchronous under the hood (in bytecode), even though they appear synchronous in the source code.
The best source to understand this transformation IMO is the talk "Deep Dive into Coroutines" by Roman Elizarov.
For example, the following function:
class MyClass {
suspend fun myFunction(arg: Int): String {
delay(100)
return "bob"
}
}
Is turned into the following (expressed in Java instead of actual JVM bytecode for simplicity):
public final class MyClass {
public final Object myFunction(int arg, #NotNull Continuation<? super String> $completion) {
// ...
}
}
This includes the following changes to the function:
The return type is changed to Java's Object (the equivalent of Kotlin's Any? - a type containing all values), to allow returning a special COROUTINE_SUSPENDED token to represent when the coroutine is actually suspended
It gets an additional Continuation<X> argument (where X is the former return type of the function that was declared in the code - in the example it's String). This continuation acts like a callback when resuming the suspend function.
Its body is turned into a state machine (instead of literally using callbacks, for efficiency). This is done by breaking down the body of the function into parts around so called suspension points, and turning those parts into the branches of a big switch. The state about the local variables and where we are in the switch is stored inside the Continuation object.
This is a very quick way to describe it, but you can see it happen with more details and with examples in the talk. This whole transformation is basically how the "suspend/resume" mechanism is implemented under the hood.
Coroutine or function gets suspended?
At a high level, we say that calling a suspending function suspends the coroutine, meaning the current thread can start executing another coroutine. So, the coroutine is said to be suspended rather than the function.
In fact, call sites of suspending functions are called "suspension points" for this reason.
Which coroutine gets suspended?
Let's look at your code and break down what happens (the numbering follows the execution timeline):
// 1. this call starts a new coroutine (let's call it C1).
// If there were code after it, it would be executed concurrently with
// the body of this async
async {
...
// 2. this is a regular function call, so we go to computation()'s body
val deferred = computation()
// 4. because await() is suspendING, it suspends coroutine C1.
// This means that if we had a single thread in our dispatcher,
// it would now be free to go execute C2
// 7. once C2 completes, C1 is resumed with the result `true` of C2's async
val result = deferred.await()
...
// 8. C1 can now keep going in the current thread until it gets
// suspended again (or not)
}
fun computation(): Deferred<Boolean> {
// 3. this async call starts a second coroutine (C2). Depending on the
// dispatcher you're using, you may have one or more threads.
// 3.a. If you have multiple threads, the block of this async could be
// executed in parallel of C1 in another thread
// 3.b. If you have only one thread, the block is sort of "queued" but
// not executed right away (as in an event loop)
//
// In both cases, we say that this block executes "concurrently"
// with C1, and computation() immediately returns the Deferred
// instance to its caller (unless a special dispatcher or
// coroutine start argument is used, but let's keep it simple).
return async {
// 5. this may now be executed
true
// 6. C2 is now completed, so the thread can go back to executing
// another coroutine (e.g. C1 here)
}
}
The outer async starts a coroutine. When it calls computation(), the inner async starts a second coroutine. Then, the call to await() suspends the execution of the outer async coroutine, until the execution of the inner async's coroutine is over.
You can even see that with a single thread: the thread will execute the outer async's beginning, then call computation() and reach the inner async. At this point, the body of the inner async is skipped, and the thread continues executing the outer async until it reaches await().
await() is a "suspension point", because await is a suspending function.
This means that the outer coroutine is suspended, and thus the thread starts executing the inner one. When it is done, it comes back to execute the end of the outer async.
Does suspend mean that while outer async coroutine is waiting (await) for the inner computation coroutine to finish, it (the outer async coroutine) idles (hence the name suspend) and returns thread to the thread pool, and when the child computation coroutine finishes, it (the outer async coroutine) wakes up, takes another thread from the pool and continues?
Yes, precisely.
The way this is actually achieved is by turning every suspending function into a state machine, where each "state" corresponds to a suspension point inside this suspend function. Under the hood, the function can be called multiple times, with the information about which suspension point it should start executing from (you should really watch the video I linked for more info about that).
To understand what exactly it means to suspend a coroutine, I suggest you go through this code:
import kotlinx.coroutines.Dispatchers.Unconfined
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlin.coroutines.Continuation
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
var continuation: Continuation<Int>? = null
fun main() {
GlobalScope.launch(Unconfined) {
val a = a()
println("Result is $a")
}
10.downTo(0).forEach {
continuation!!.resume(it)
}
}
suspend fun a(): Int {
return b()
}
suspend fun b(): Int {
while (true) {
val i = suspendCoroutine<Int> { cont -> continuation = cont }
if (i == 0) {
return 0
}
}
}
The Unconfined coroutine dispatcher eliminates the magic of coroutine dispatching and allows us to focus directly on bare coroutines.
The code inside the launch block starts executing right away on the current thread, as a part of the launch call. What happens is as follows:
Evaluate val a = a()
This chains to b(), reaching suspendCoroutine.
Function b() executes the block passed to suspendCoroutine and then returns a special COROUTINE_SUSPENDED value. This value is not observable through the Kotlin programming model, but that's what the compiled Java method does.
Function a(), seeing this return value, itself also returns it.
The launch block does the same and control now returns to the line after the launch invocation: 10.downTo(0)...
Note that, at this point, you have the same effect as if the code inside the launch block and your fun main code are executing concurrently. It just happens that all this is happening on a single native thread so the launch block is "suspended".
Now, inside the forEach looping code, the program reads the continuation that the b() function wrote and resumes it with the value of 10. resume() is implemented in such a way that it will be as if the suspendCoroutine call returned with the value you passed in. So you suddenly find yourself in the middle of executing b(). The value you passed to resume() gets assigned to i and checked against 0. If it's not zero, the while (true) loop goes on inside b(), again reaching suspendCoroutine, at which point your resume() call returns, and now you go through another looping step in forEach(). This goes on until finally you resume with 0, then the println statement runs and the program completes.
The above analysis should give you the important intuition that "suspending a coroutine" means returning the control back to the innermost launch invocation (or, more generally, coroutine builder). If a coroutine suspends again after resuming, the resume() call ends and control returns to the caller of resume().
The presence of a coroutine dispatcher makes this reasoning less clear-cut because most of them immediately submit your code to another thread. In that case the above story happens in that other thread, and the coroutine dispatcher also manages the continuation object so it can resume it when the return value is available.
As many good answers are already there, I would like to post a simpler example for others.
runBlocking use case :
myMethod() is suspend function
runBlocking { } starts a Coroutine in blocking way. It is similar to how we were blocking normal threads with Thread class and notifying blocked threads after certain events.
runBlocking { } does block the current executing thread, until the coroutine (body between {}) gets completed
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main_activity)
Log.i(TAG,"Outer code started on Thread : " + Thread.currentThread().name);
runBlocking {
Log.d(TAG,"Inner code started on Thread : " + Thread.currentThread().name + " making outer code suspend");
myMethod();
}
Log.i(TAG,"Outer code resumed on Thread : " + Thread.currentThread().name);
}
private suspend fun myMethod() {
withContext(Dispatchers.Default) {
for(i in 1..5) {
Log.d(TAG,"Inner code i : $i on Thread : " + Thread.currentThread().name);
}
}
This outputs :
I/TAG: Outer code started on Thread : main
D/TAG: Inner code started on Thread : main making outer code suspend
// ---- main thread blocked here, it will wait until coroutine gets completed ----
D/TAG: Inner code i : 1 on Thread : DefaultDispatcher-worker-2
D/TAG: Inner code i : 2 on Thread : DefaultDispatcher-worker-2
D/TAG: Inner code i : 3 on Thread : DefaultDispatcher-worker-2
D/TAG: Inner code i : 4 on Thread : DefaultDispatcher-worker-2
D/TAG: Inner code i : 5 on Thread : DefaultDispatcher-worker-2
// ---- main thread resumes as coroutine is completed ----
I/TAG: Outer code resumed on Thread : main
launch use case :
launch { } starts a coroutine concurrently.
This means that when we specify launch, a coroutine starts execution on worker thread.
The worker thread and outer thread (from which we called launch { }) both runs concurrently. Internally, JVM may perform Preemptive Threading
When we require multiple tasks to run in parallel, we can use this. There are scopes which specify lifetime of coroutine. If we specify GlobalScope, the coroutine will work until application lifetime ends.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main_activity)
Log.i(TAG,"Outer code started on Thread : " + Thread.currentThread().name);
GlobalScope.launch(Dispatchers.Default) {
Log.d(TAG,"Inner code started on Thread : " + Thread.currentThread().name + " making outer code suspend");
myMethod();
}
Log.i(TAG,"Outer code resumed on Thread : " + Thread.currentThread().name);
}
private suspend fun myMethod() {
withContext(Dispatchers.Default) {
for(i in 1..5) {
Log.d(TAG,"Inner code i : $i on Thread : " + Thread.currentThread().name);
}
}
}
This Outputs :
10806-10806/com.example.viewmodelapp I/TAG: Outer code started on Thread : main
10806-10806/com.example.viewmodelapp I/TAG: Outer code resumed on Thread : main
// ---- In this example, main had only 2 lines to execute. So, worker thread logs start only after main thread logs complete
// ---- In some cases, where main has more work to do, the worker thread logs get overlap with main thread logs
10806-10858/com.example.viewmodelapp D/TAG: Inner code started on Thread : DefaultDispatcher-worker-1 making outer code suspend
10806-10858/com.example.viewmodelapp D/TAG: Inner code i : 1 on Thread : DefaultDispatcher-worker-1
10806-10858/com.example.viewmodelapp D/TAG: Inner code i : 2 on Thread : DefaultDispatcher-worker-1
10806-10858/com.example.viewmodelapp D/TAG: Inner code i : 3 on Thread : DefaultDispatcher-worker-1
10806-10858/com.example.viewmodelapp D/TAG: Inner code i : 4 on Thread : DefaultDispatcher-worker-1
10806-10858/com.example.viewmodelapp D/TAG: Inner code i : 5 on Thread : DefaultDispatcher-worker-1
async and await use case :
When we have multiple tasks to do and they depend on other's completion, async and await would help.
For example, in below code, there are 2 suspend functions myMethod() and myMethod2(). myMethod2() should get executed only after full completion of myMethod() OR myMethod2() depends on result of myMethod(), we can use async and await
async starts a coroutine in parallel similar to launch. But, it provides a way to wait for one coroutine before starting another coroutine in parallel.
That way is await(). async returns an instance of Deffered<T>. T would be Unit for default. When we need to wait for any async's completion, we need to call .await() on Deffered<T> instance of that async. Like in below example, we called innerAsync.await() which implies that the execution would get suspended until innerAsync gets completed. We can observe the same in output. The innerAsync gets completed first, which calls myMethod(). And then next async innerAsync2 starts, which calls myMethod2()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main_activity)
Log.i(TAG,"Outer code started on Thread : " + Thread.currentThread().name);
job = GlobalScope.launch(Dispatchers.Default) {
innerAsync = async {
Log.d(TAG, "Inner code started on Thread : " + Thread.currentThread().name + " making outer code suspend");
myMethod();
}
innerAsync.await()
innerAsync2 = async {
Log.w(TAG, "Inner code started on Thread : " + Thread.currentThread().name + " making outer code suspend");
myMethod2();
}
}
Log.i(TAG,"Outer code resumed on Thread : " + Thread.currentThread().name);
}
private suspend fun myMethod() {
withContext(Dispatchers.Default) {
for(i in 1..5) {
Log.d(TAG,"Inner code i : $i on Thread : " + Thread.currentThread().name);
}
}
}
private suspend fun myMethod2() {
withContext(Dispatchers.Default) {
for(i in 1..10) {
Log.w(TAG,"Inner code i : $i on Thread : " + Thread.currentThread().name);
}
}
}
This outputs :
11814-11814/? I/TAG: Outer code started on Thread : main
11814-11814/? I/TAG: Outer code resumed on Thread : main
11814-11845/? D/TAG: Inner code started on Thread : DefaultDispatcher-worker-2 making outer code suspend
11814-11845/? D/TAG: Inner code i : 1 on Thread : DefaultDispatcher-worker-2
11814-11845/? D/TAG: Inner code i : 2 on Thread : DefaultDispatcher-worker-2
11814-11845/? D/TAG: Inner code i : 3 on Thread : DefaultDispatcher-worker-2
11814-11845/? D/TAG: Inner code i : 4 on Thread : DefaultDispatcher-worker-2
11814-11845/? D/TAG: Inner code i : 5 on Thread : DefaultDispatcher-worker-2
// ---- Due to await() call, innerAsync2 will start only after innerAsync gets completed
11814-11848/? W/TAG: Inner code started on Thread : DefaultDispatcher-worker-4 making outer code suspend
11814-11848/? W/TAG: Inner code i : 1 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 2 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 3 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 4 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 5 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 6 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 7 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 8 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 9 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 10 on Thread : DefaultDispatcher-worker-4
I've found that the best way to understand suspend is to make an analogy between this keyword and coroutineContext property.
Kotlin functions can be declared as local or global. Local functions magically have access to this keyword while global don't.
Kotlin functions can be declared as suspend or blocking. suspend functions magically have access to coroutineContext property while blocking functions don't.
The thing is: coroutineContext property
is declared like a "normal" property in Kotlin stdlib but this declaration is just a stub for documentation/navigation purposes. In fact coroutineContext is builtin intrinsic property that means under the hood compiler magic aware of this property like it aware of language keywords.
What this keyword does for local functions is what coroutineContext property does for suspend functions: it gives access to current context of execution.
So, you need suspend to get an access to coroutineContext property - the instance of currently executed coroutine context
I wanted to give you a simple example of the concept of continuation. This is what a suspend function does, it can freeze/suspend and then it continues/resumes. Stop thinking of coroutine in terms of threads and Semaphore. Think of it in terms of continuation and even callback hooks.
To be clear, a coroutine can be paused by using a suspend function. lets investigate this:
In android we could do this for example :
var TAG = "myTAG:"
fun myMethod() { // function A in image
viewModelScope.launch(Dispatchers.Default) {
for (i in 10..15) {
if (i == 10) { //on first iteration, we will completely FREEZE this coroutine (just for loop here gets 'suspended`)
println("$TAG im a tired coroutine - let someone else print the numbers async. i'll suspend until your done")
freezePleaseIAmDoingHeavyWork()
} else
println("$TAG $i")
}
}
//this area is not suspended, you can continue doing work
}
suspend fun freezePleaseIAmDoingHeavyWork() { // function B in image
withContext(Dispatchers.Default) {
async {
//pretend this is a big network call
for (i in 1..10) {
println("$TAG $i")
delay(1_000)//delay pauses coroutine, NOT the thread. use Thread.sleep if you want to pause a thread.
}
println("$TAG phwww finished printing those numbers async now im tired, thank you for freezing, you may resume")
}
}
}
Above code prints the following :
I: myTAG: my coroutine is frozen but i can carry on to do other things
I: myTAG: im a tired coroutine - let someone else print the numbers async. i'll suspend until your done
I: myTAG: 1
I: myTAG: 2
I: myTAG: 3
I: myTAG: 4
I: myTAG: 5
I: myTAG: 6
I: myTAG: 7
I: myTAG: 8
I: myTAG: 9
I: myTAG: 10
I: myTAG: phwww finished printing those numbers async now im tired, thank you for freezing, you may resume
I: myTAG: 11
I: myTAG: 12
I: myTAG: 13
I: myTAG: 14
I: myTAG: 15
imagine it working like this:
So the current function you launched from does not stop, just a coroutine would suspend while it continues. The thread is not paused by running a suspend function.
I think this site can help you straight things out and is my reference.
Let's do something cool and freeze our suspend function in the middle of an iteration. We will resume it later in onResume
Store a variable called continuation and we'll load it with the coroutines continuation object for us :
var continuation: CancellableContinuation<String>? = null
suspend fun freezeHere() = suspendCancellableCoroutine<String> {
continuation = it
}
fun unFreeze() {
continuation?.resume("im resuming") {}
}
Now, let's return to our suspended function and make it freeze in middle of iteration :
suspend fun freezePleaseIAmDoingHeavyWork() {
withContext(Dispatchers.Default) {
async {
//pretend this is a big network call
for (i in 1..10) {
println("$TAG $i")
delay(1_000)
if(i == 3)
freezeHere() //dead pause, do not go any further
}
}
}
}
Then somewhere else like in onResume (for example):
override fun onResume() {
super.onResume()
unFreeze()
}
And the loop will continue. Its pretty neat to know we can freeze a suspend function at any point and resume it after some time has beeb passed. You can also look into channels
There are a lot of great answers here, but I think there are two additional things that are important to note.
launch / withContext / runBlocking and a lot of other things in the examples are from the coroutines library. which actually have nothing to do with suspend. you don't need the coroutines library to use coroutines. Coroutines are a compiler "trick". Yes, the library sure makes things easier, but the compiler is doing the magic of suspending & resuming things.
The second thing, is the compiler is just taking code that looks procedural and turning it into callbacks under the hood.
Take the following minimal coroutine that suspends that does not use the coroutine library :
lateinit var context: Continuation<Unit>
suspend {
val extra="extra"
println("before suspend $extra")
suspendCoroutine<Unit> { context = it }
println("after suspend $extra")
}.startCoroutine(
object : Continuation<Unit> {
override val context: CoroutineContext = EmptyCoroutineContext
// called when a coroutine ends. do nothing.
override fun resumeWith(result: Result<Unit>) {
result.onFailure { ex : Throwable -> throw ex }
}
}
)
println("kick it")
context.resume(Unit)
I think an important way to understand it is to look at what the compiler does with this code. effectively it creates a class for the lambda. it creates a property in the class for the "extra" string, then it creates two function, one that prints the "before" and another the prints the "after".
Effectively the compiler took what looks like procedural code and turned it into callbacks.
So what does the suspend keyword do? It tell the compiler how far back to look for context that the generated callbacks will need. The compiler needs to know which variables are used in which "callbacks", and the suspend keyword helps it. In this example the "extra" variable is used both before and after the suspend. So it needs to be pulled out to a property of the class containing the callbacks the compiler makes.
It also tells the compiler that this is the "beginning" of state and to prepare to split up the following code into callbacks. The startCoroutine only exists on suspend lambda.
The actual Java code generated by the Kotlin compiler is here. It's a switch statement instead of callbacks, but it's effectively the same thing. Called first w/ case 0, then w/ case 1 after the resume.
#Nullable
public final Object invokeSuspend(#NotNull Object $result) {
var10_2 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
switch (this.label) {
case 0: {
ResultKt.throwOnFailure((Object)$result);
extra = "extra";
var3_4 = "before delay " + extra;
var4_9 = false;
System.out.println((Object)var3_4);
var3_5 = this;
var4_9 = false;
var5_10 = false;
this.L$0 = extra;
this.L$1 = var3_5;
this.label = 1;
var5_11 = var3_5;
var6_12 = false;
var7_13 = new SafeContinuation(IntrinsicsKt.intercepted((Continuation)var5_11));
it = (Continuation)var7_13;
$i$a$-suspendCoroutine-AppKt$main$1$1 = false;
this.$context.element = it;
v0 = var7_13.getOrThrow();
if (v0 == IntrinsicsKt.getCOROUTINE_SUSPENDED()) {
DebugProbesKt.probeCoroutineSuspended((Continuation)var3_5);
}
v1 = v0;
if (v0 == var10_2) {
return var10_2;
}
** GOTO lbl33
}
case 1: {
var3_6 = this.L$1;
extra = (String)this.L$0;
ResultKt.throwOnFailure((Object)$result);
v1 = $result;
lbl33:
// 2 sources
var3_8 = "after suspend " + extra;
var4_9 = false;
System.out.println((Object)var3_8);
return Unit.INSTANCE;
}
}
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
Let's say we have a function named myFunction.
fun myFunction(){
Code block 1
Code block 2 //this one has a long running operation
Code block 3
Code block 4
}
Usually these code blocks execute like block1, block2, block3, block4 . So code block 3 and 4 might execute while code block 2 is still running. Because of that reason there can be problems. (screen might freeze, app might crash)
But if we make this function suspend
suspend fun MyFunction(){
Code block 1
Code block 2 //this one has a long running operation
Code block 3
Code block 4
}
Now, this function can get paused when code block 2(long running operation) starts executing and get resumed when it is done. Code block 3 and 4 will execute after that. So there will be no unexpected thread sharing issues.
For anyone still wondering how do we actually suspend a suspend function, we use the suspendCoroutine function in the body of the suspend function .
suspend fun foo() :Int
{
Log.d(TAG,"Starting suspension")
return suspendCoroutine<Int> { num->
val result = bar()
Log.d(TAG,"Starting resumption")
num.resumeWith(Result.success(result))
}
}
fun bar():Int //this is a long runnning task