How to create this coroutines in kotlin? - kotlin

I am trying to print Hello in between World 4 from this code.
import kotlinx.coroutines.*
import java.util.*
fun main() = runBlocking <Unit> {
launch { //COR-1
var now = Date().time
val c= timeConusmingFunct(now) //may be some IO work
print(" World $c") //once done lets process it
}
launch{ //COR-2
print(" in between")
}
print("Hello ")
}
suspend fun timeConusmingFunct(now: Long): Int{
while(Date().time-now <4000){
//a way to block the thread
}
return 4
}
My understanding is that when main thread execute it will face COR-1 and move onto next COR-2 and then move to final print("Hello"). As COR-1 will take some time (~4s) and the time consuming funct is marked suspend [which indicates that it will take some time].
The call should have gone to COR-2.
However it prints : Hello World 4 in between
Why this happens, and how do I make my code work as intended Hello in between World 4 with help of coroutine ?

You need this minimal change to your spin-waiting loop:
while (Date().time - now < 4000) {
yield()
}
The concurrency in coroutines is cooperative, suspension happens when the code explicitly asks for it. Normally it happens transparently because you call a suspendable function, but in your case you must add it in since you aren't otherwise calling any suspendable functions.
what if I had some Image processing in that timeConsumingcode to do. It will block the main thread. I want it to be handled by co routine.
Image processing is a CPU-bound task and, since it's not a task that must be pinned to the GUI thread, you are better off handing it over to a thread pool. The most convenient way to do it is to write
withContext(Dispatchers.Default) {
processImage(img)
}
This way your coroutine, that otherwise runs on the main GUI thread, will temporarily jump over to the thread pool to do the long task, and then move back to the GUI thread to go on. While it's running on the thread pool, the GUI thread will be able to serve other GUI events and keep your UI live.

Related

How to cancel kotlin coroutine with potentially "un-cancellable" method call inside it?

I have this piece of code:
// this method is used to evaluate the input string, and it returns evaluation result in string format
fun process(input: String): String {
val timeoutMillis = 5000L
val page = browser.newPage()
try {
val result = runBlocking {
withTimeout(timeoutMillis) {
val result = page.evaluate(input).toString()
return#withTimeout result
}
}
return result
} catch (playwrightException: PlaywrightException) {
return "Could not parse template! '${playwrightException.localizedMessage}'"
} catch (timeoutException: TimeoutCancellationException) {
return "Could not parse template! (timeout)"
} finally {
page.close()
}
}
It should throw exception after 5 seconds if the method is taking too long to execute (example: input potentially contains infinite loop) but it doesent (becomes deadlock I assume) coz coroutines should be cooperative. But the method I am calling is from another library and I have no control over its computation (for sticking yield() or smth like it).
So the question is: is it even possible to timeout such coroutine? if yes, then how?
Should I use java thread insted and just kill it after some time?
But the method I am calling is from another library and I have no control over its computation (for sticking yield() or smth like it).
If that is the case, I see mainly 2 situations here:
the library is aware that this is a long-running operation and supports thread interrupts to cancel it. This is the case for Thread.sleep and some I/O operations.
the library function really does block the calling thread for the whole time of the operation, and wasn't designed to handle thread interrupts
Situation 1: the library function is interruptible
If you are lucky enough to be in situation 1, then simply wrap the library's call into a runInterruptible block, and the coroutines library will translate cancellation into thread interruptions:
fun main() {
runBlocking {
val elapsed = measureTimeMillis {
withTimeoutOrNull(100.milliseconds) {
runInterruptible {
interruptibleBlockingCall()
}
}
}
println("Done in ${elapsed}ms")
}
}
private fun interruptibleBlockingCall() {
Thread.sleep(3000)
}
Situation 2: the library function is NOT interruptible
In the more likely situation 2, you're kind of out of luck.
Should I use java thread insted and just kill it after some time?
There is no such thing as "killing a thread" in Java. See Why is Thread.stop deprecated?, or How do you kill a Thread in Java?.
In short, in that case you do not have a choice but to block some thread.
I do not know a solution to this problem that doesn't leak resources. Using an ExecutorService would not help if the task doesn't support thread interrupts - the threads will not die even with shutdownNow() (which uses interrupts).
Of course, the blocked thread doesn't have to be your thread. You can technically launch a separate coroutine on another thread (using another dispatcher if yours is single-threaded), to wrap the libary function call, and then join() the job inside a withTimeout to avoid waiting for it forever. That is however probably bad, because you're basically deferring the problem to whichever scope you use to launch the uncancellable task (this is actually why we can't use a simple withContext here).
If you use GlobalScope or another long-running scope, you effectively leak the hanging coroutine (without knowing for how long).
If you use a more local parent scope, you defer the problem to that scope. This is for instance the case if you use the scope of an enclosing runBlocking (like in your example), which makes this solution pointless:
fun main() {
val elapsed = measureTimeMillis {
doStuff()
}
println("Completely done in ${elapsed}ms")
}
private fun doStuff() {
runBlocking {
val nonCancellableJob = launch(Dispatchers.IO) {
uncancellableBlockingCall()
}
val elapsed = measureTimeMillis {
withTimeoutOrNull(100.milliseconds) {
nonCancellableJob.join()
}
}
println("Done waiting in ${elapsed}ms")
} // /!\ runBlocking will still wait here for the uncancellable child coroutine
}
// Thread.sleep is in fact interruptible but let's assume it's not for the sake of the example
private fun uncancellableBlockingCall() {
Thread.sleep(3000)
}
Outputs something like:
Done waiting in 122ms
Completely done in 3055ms
So the bottom line is either live with this long thing potentially hanging, or ask the developers of that library to handle interruption or make the task cancellable.

In which aspects runBlocking is worse than suspend?

It's quite clearly stated in official documentation that runBlocking "should not be used from a coroutine". I roughly get the idea, but I'm trying to find an example where using runBlocking instead of suspend functions negatively impacts performance.
So I created an example like this:
import java.time.Instant
import java.time.format.DateTimeFormatter
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import kotlin.time.Duration.Companion.seconds
private val time = 1.seconds
private suspend fun getResource(name: String): String {
log("Starting getting ${name} for ${time}...")
delay(time)
log("Finished getting ${name}!")
return "Resource ${name}"
}
fun main(args: Array<String>) = runBlocking {
val resources = listOf("A", "B")
.map { async { getResource(it) } }
.awaitAll()
log(resources)
}
fun log(msg: Any) {
val now = DateTimeFormatter.ISO_INSTANT.format(Instant.now())
println("$now ${Thread.currentThread()}: $msg")
}
This gives the expected output of:
2022-04-29T15:52:35.943156Z Thread[main,5,main]: Starting getting A for 1s...
2022-04-29T15:52:35.945570Z Thread[main,5,main]: Starting getting B for 1s...
2022-04-29T15:52:36.947539Z Thread[main,5,main]: Finished getting A!
2022-04-29T15:52:36.948334Z Thread[main,5,main]: Finished getting B!
2022-04-29T15:52:36.949233Z Thread[main,5,main]: [Resource A, Resource B]
From my understanding getResource(A) was started and the moment it arrived at delay it gave the control back and then getResource(B) was started. Then they both waited in a single thread and when the time passed, they both were again executed - everything in one second as expected.
So now I wanted to "break" it a little and replaced getResource with:
private fun getResourceBlocking(name: String): String = runBlocking {
log("Starting getting ${name} for ${time}...")
delay(time)
log("Finished getting ${name}!")
"Resource ${name}"
}
and called it from the main method in place of getResource.
and then again I got:
2022-04-29T15:58:41.908015Z Thread[main,5,main]: Starting getting A for 1s...
2022-04-29T15:58:41.910532Z Thread[main,5,main]: Starting getting B for 1s...
2022-04-29T15:58:42.911661Z Thread[main,5,main]: Finished getting A!
2022-04-29T15:58:42.912126Z Thread[main,5,main]: Finished getting B!
2022-04-29T15:58:42.912876Z Thread[main,5,main]: [Resource A, Resource B]
So it still took only 1 second to run and B was started before A finished. At the same time there doesn't seem to be any additional threads spawned (everything is in Thread[main,5,main]).
So how does this work? How calling blocking functions in async makes it execute "concurrently" in a single thread anyway?
Your reasoning is correct, but you accidentally hit a very special case of using runBlocking(), which was intentionally optimized to not degrade the performance. If you use dispatcher-less runBlocking() inside another dispatcher-less runBlocking(), then the inner runBlocking() tries to re-use the event loop created by the outer one. So inner runBlocking() actually works similarly as it is suspending and not blocking (but this is not 100% accurate).
In a real case where the outer coroutine would not be itself created with runBlocking() or if you use some real dispatchers, you would see the degraded performance. You can replace the outer code with this:
val dispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
fun main(args: Array<String>) = runBlocking(dispatcher) { ... }
Then resources are loaded sequentially, as you probably expected. But even with this change, getResource() still loads resources concurrently.
The goal with suspending is not to finish the work faster. The goal with suspending is to not block the current thread while waiting for the suspend function to return.
In your example, it doesn't matter if the current thread is blocked or not because it's not doing anything else while waiting.
In an app with a UI, you are usually concerned about blocking on the UI thread (AKA main thread), because that will freeze the app. Nothing can be animated, scrolled, or clicked while the UI thread is blocked.
If you call a suspend function from the main thread, the main thread will not be blocked while it waits for the suspend function to return.
The reason no other threads are spawned in your example is that you are using runBlocking which runs on the current thread by default without doing its work on a background thread. In an actual application, you would be launching coroutines from a CoroutineScope rather than from a runBlocking.

Is coroutineScope just like block function in Kotlin? [duplicate]

I was reading Coroutine Basics trying to understand and learn it.
There is a part there with this code:
fun main() = runBlocking { // this: CoroutineScope
launch {
delay(200L)
println("Task from runBlocking")
}
coroutineScope { // Creates a new coroutine scope
launch {
delay(900L)
println("Task from nested launch")
}
delay(100L)
println("Task from coroutine scope") // This line will be printed before nested launch
}
println("Coroutine scope is over") // This line is not printed until nested launch completes
}
The output goes like so:
Task from coroutine scope
Task from runBlocking
Task from nested launch
Coroutine scope is over
My question is why this line:
println("Coroutine scope is over") // This line is not printed until nested launch completes
is called always last?
Shouldn't it be called since the:
coroutineScope { // Creates a new coroutine scope
....
}
is suspended?
There is also a note there:
The main difference between runBlocking and coroutineScope is that the latter does not block the current thread while waiting for all children to complete.
I dont understand how coroutineScope and runBlocking are different here? coroutineScope looks like its blocking since it only gets to the last line when it is done.
Can anyone enlighten me here?
I don't understand how coroutineScope and runBlocking are different here? coroutineScope looks like its blocking since it only gets to the last line when it is done.
There are two separate worlds: the suspendable world (within a coroutine) and the non-suspendable one. As soon as you enter the body of runBlocking, you are in the suspendable world, where suspend funs behave like blocking code and you can't get to the next line until the suspend fun returns. coroutineScope is a suspend fun that returns only when all the coroutines inside it are done. Therefore the last line must print at the end.
I copied the above explanation from a comment which seems to have clicked with readers. Here is the original answer:
From the perspective of the code in the block, your understanding is correct. The difference between runBlocking and coroutineScope happens at a lower level: what's happening to the thread while the coroutine is blocked?
runBlocking is not a suspend fun. The thread that called it remains inside it until the coroutine is complete.
coroutineScope is a suspend fun. If your coroutine suspends, the coroutineScope function gets suspended as well. This allows the top-level function, a non-suspending function that created the coroutine, to continue executing on the same thread. The thread has "escaped" the coroutineScope block and is ready to do some other work.
In your specific example: when your coroutineScope suspends, control returns to the implementation code inside runBlocking. This code is an event loop that drives all the coroutines you started within it. In your case, there will be some coroutines scheduled to run after a delay. When the time arrives, it will resume the appropriate coroutine, which will run for a short while, suspend, and then control will be again inside runBlocking.
While the above describes the conceptual similarities, it should also show you that runBlocking is a completely different tool from coroutineScope.
runBlocking is a low-level construct, to be used only in framework code or self-contained examples like yours. It turns an existing thread into an event loop and creates its coroutine with a Dispatcher that posts resuming coroutines to the event loop's queue.
coroutineScope is a user-facing construct, used to delineate the boundaries of a task that is being parallel-decomposed inside it. You use it to conveniently await on all the async work happening inside it, get the final result, and handle all failures at one central place.
The chosen answer is good but fails to address some other important aspects of the sample code that was provided. For instance, launch is non-blocking and is suppose to execute immediately. That is simply not true. The launch itself returns immediately BUT the code inside the launch does appear to be put into a queue and is only executed when any other launches that were previously put into the queue have completed.
Here's a similar piece of sample code with all the delays removed and an additional launch included. Without looking at the result below, see if you can predict the order in which the numbers are printed. Chances are that you will fail:
import kotlinx.coroutines.*
fun main() = runBlocking {
launch {
println("1")
}
coroutineScope {
launch {
println("2")
}
println("3")
}
coroutineScope {
launch {
println("4")
}
println("5")
}
launch {
println("6")
}
for (i in 7..100) {
println(i.toString())
}
println("101")
}
The result is:
3
1
2
5
4
7
8
9
10
...
99
100
101
6
The fact that number 6 is printed last, even after going through nearly 100 println have been executed, indicates that the code inside the last launch never gets executed until all non-blocking code after the launch has completed. But that is not really true either, because if that were the case, the first launch should not have executed until numbers 7 to 101 have completed. Bottom line? Mixing launch and coroutineScope is highly unpredictable and should be avoided if you expect a certain order in the way things should be executed.
To prove that code inside launches is placed into a queue and ONLY executed after ALL the non-blocking code has completed, run this (no coroutineScopes are used):
import kotlinx.coroutines.*
fun main() = runBlocking {
launch {
println("1")
}
launch {
println("2")
}
launch {
println("3")
}
for (i in 4..100) {
println(i.toString())
}
println("101")
}
This is the result you get:
4
5
6
...
101
1
2
3
Adding a CoroutineScope will break this behavior. It will cause all non-blocking code that follows the CoroutineScope to not be executed until ALL code prior to the CoroutineScope has completed.
It should also be noted that in this code sample, each of the launches in the queue are executed sequentially in the order that they are added to the queue and each launch will only execute AFTER the previous launch executes. This may make it appear that all launches share a common thread. This is not true. Each of them is given their own thread. However, if any code inside a launch calls a suspend function, the next launch in the queue is started immediately while the suspend function is being carried out. To be honest, this is very strange behavior. Why not just run all the launches in the queue asynchronously? While I don't know the internals of what goes on in this queue, my guess is that each launch in the queue does not get its own thread but all share a common thread. It is only when a suspend function is encountered does it appear that a new thread is created for the next launch in the queue. It may be done this way to save on resources.
To summarize, execution is done in this order:
Code inside a launch is placed inside a queue and are executed in the order that they are added.
Non-blocking code following a launch executes immediately before anything in the queue is executed.
A CoroutineScope blocks ALL code following it BUT will execute all the launch coroutines in the queue before resuming to the code following the CoroutineScope.
runBlocking is for you to block the main thread.
coroutineScope is for you to block the runBlocking.
Well, after having read all the answers here, I found none of them answered the question beyond repeating the wording of the fragments of the documentation.
So, I went on to search for an answer elsewhere and found it here. It practically shows the difference in behavior of coroutineScope and runBlocking (i.e. the difference between suspending and blocking)
runBlocking just blocks the current thread until inner coroutines will be completed. Here, thread that executes runBlocking will be blocked until the coroutine from coroutineScope will be finished.
First launch just won't allow the thread execute instructions that come after runBlocking, but will allow proceed to the instructions that come immediately after this launch block - that's why Task from coroutine scope is printed before than Task from runBlocking.
But nested coroutineScope in the context of runBlocking won't allow the thread to execute instructions that come after this coroutineScope block, because runBlocking will block the thread until the coroutine from coroutineScope will be finished completely. And that's why Coroutine scope is over will always come after Task from nested launch.
From this wonderful article https://jivimberg.io/blog/2018/05/04/parallel-map-in-kotlin/
suspend fun <A, B> Iterable<A>.pmap(f: suspend (A) -> B): List<B> = coroutineScope {
map { async { f(it) } }.awaitAll()
}
With runBlocking, we were not using Structured Concurrency, so an invocation of f could fail and all other executions would continue unfazed. And also we were not playing nice with the rest of the code. By using runBlocking we were forcefully blocking the thread until the whole execution of pmap finishes, instead of letting the caller decide how the execution should go.

Coroutines: runBlocking vs coroutineScope

I was reading Coroutine Basics trying to understand and learn it.
There is a part there with this code:
fun main() = runBlocking { // this: CoroutineScope
launch {
delay(200L)
println("Task from runBlocking")
}
coroutineScope { // Creates a new coroutine scope
launch {
delay(900L)
println("Task from nested launch")
}
delay(100L)
println("Task from coroutine scope") // This line will be printed before nested launch
}
println("Coroutine scope is over") // This line is not printed until nested launch completes
}
The output goes like so:
Task from coroutine scope
Task from runBlocking
Task from nested launch
Coroutine scope is over
My question is why this line:
println("Coroutine scope is over") // This line is not printed until nested launch completes
is called always last?
Shouldn't it be called since the:
coroutineScope { // Creates a new coroutine scope
....
}
is suspended?
There is also a note there:
The main difference between runBlocking and coroutineScope is that the latter does not block the current thread while waiting for all children to complete.
I dont understand how coroutineScope and runBlocking are different here? coroutineScope looks like its blocking since it only gets to the last line when it is done.
Can anyone enlighten me here?
I don't understand how coroutineScope and runBlocking are different here? coroutineScope looks like its blocking since it only gets to the last line when it is done.
There are two separate worlds: the suspendable world (within a coroutine) and the non-suspendable one. As soon as you enter the body of runBlocking, you are in the suspendable world, where suspend funs behave like blocking code and you can't get to the next line until the suspend fun returns. coroutineScope is a suspend fun that returns only when all the coroutines inside it are done. Therefore the last line must print at the end.
I copied the above explanation from a comment which seems to have clicked with readers. Here is the original answer:
From the perspective of the code in the block, your understanding is correct. The difference between runBlocking and coroutineScope happens at a lower level: what's happening to the thread while the coroutine is blocked?
runBlocking is not a suspend fun. The thread that called it remains inside it until the coroutine is complete.
coroutineScope is a suspend fun. If your coroutine suspends, the coroutineScope function gets suspended as well. This allows the top-level function, a non-suspending function that created the coroutine, to continue executing on the same thread. The thread has "escaped" the coroutineScope block and is ready to do some other work.
In your specific example: when your coroutineScope suspends, control returns to the implementation code inside runBlocking. This code is an event loop that drives all the coroutines you started within it. In your case, there will be some coroutines scheduled to run after a delay. When the time arrives, it will resume the appropriate coroutine, which will run for a short while, suspend, and then control will be again inside runBlocking.
While the above describes the conceptual similarities, it should also show you that runBlocking is a completely different tool from coroutineScope.
runBlocking is a low-level construct, to be used only in framework code or self-contained examples like yours. It turns an existing thread into an event loop and creates its coroutine with a Dispatcher that posts resuming coroutines to the event loop's queue.
coroutineScope is a user-facing construct, used to delineate the boundaries of a task that is being parallel-decomposed inside it. You use it to conveniently await on all the async work happening inside it, get the final result, and handle all failures at one central place.
The chosen answer is good but fails to address some other important aspects of the sample code that was provided. For instance, launch is non-blocking and is suppose to execute immediately. That is simply not true. The launch itself returns immediately BUT the code inside the launch does appear to be put into a queue and is only executed when any other launches that were previously put into the queue have completed.
Here's a similar piece of sample code with all the delays removed and an additional launch included. Without looking at the result below, see if you can predict the order in which the numbers are printed. Chances are that you will fail:
import kotlinx.coroutines.*
fun main() = runBlocking {
launch {
println("1")
}
coroutineScope {
launch {
println("2")
}
println("3")
}
coroutineScope {
launch {
println("4")
}
println("5")
}
launch {
println("6")
}
for (i in 7..100) {
println(i.toString())
}
println("101")
}
The result is:
3
1
2
5
4
7
8
9
10
...
99
100
101
6
The fact that number 6 is printed last, even after going through nearly 100 println have been executed, indicates that the code inside the last launch never gets executed until all non-blocking code after the launch has completed. But that is not really true either, because if that were the case, the first launch should not have executed until numbers 7 to 101 have completed. Bottom line? Mixing launch and coroutineScope is highly unpredictable and should be avoided if you expect a certain order in the way things should be executed.
To prove that code inside launches is placed into a queue and ONLY executed after ALL the non-blocking code has completed, run this (no coroutineScopes are used):
import kotlinx.coroutines.*
fun main() = runBlocking {
launch {
println("1")
}
launch {
println("2")
}
launch {
println("3")
}
for (i in 4..100) {
println(i.toString())
}
println("101")
}
This is the result you get:
4
5
6
...
101
1
2
3
Adding a CoroutineScope will break this behavior. It will cause all non-blocking code that follows the CoroutineScope to not be executed until ALL code prior to the CoroutineScope has completed.
It should also be noted that in this code sample, each of the launches in the queue are executed sequentially in the order that they are added to the queue and each launch will only execute AFTER the previous launch executes. This may make it appear that all launches share a common thread. This is not true. Each of them is given their own thread. However, if any code inside a launch calls a suspend function, the next launch in the queue is started immediately while the suspend function is being carried out. To be honest, this is very strange behavior. Why not just run all the launches in the queue asynchronously? While I don't know the internals of what goes on in this queue, my guess is that each launch in the queue does not get its own thread but all share a common thread. It is only when a suspend function is encountered does it appear that a new thread is created for the next launch in the queue. It may be done this way to save on resources.
To summarize, execution is done in this order:
Code inside a launch is placed inside a queue and are executed in the order that they are added.
Non-blocking code following a launch executes immediately before anything in the queue is executed.
A CoroutineScope blocks ALL code following it BUT will execute all the launch coroutines in the queue before resuming to the code following the CoroutineScope.
runBlocking is for you to block the main thread.
coroutineScope is for you to block the runBlocking.
Well, after having read all the answers here, I found none of them answered the question beyond repeating the wording of the fragments of the documentation.
So, I went on to search for an answer elsewhere and found it here. It practically shows the difference in behavior of coroutineScope and runBlocking (i.e. the difference between suspending and blocking)
runBlocking just blocks the current thread until inner coroutines will be completed. Here, thread that executes runBlocking will be blocked until the coroutine from coroutineScope will be finished.
First launch just won't allow the thread execute instructions that come after runBlocking, but will allow proceed to the instructions that come immediately after this launch block - that's why Task from coroutine scope is printed before than Task from runBlocking.
But nested coroutineScope in the context of runBlocking won't allow the thread to execute instructions that come after this coroutineScope block, because runBlocking will block the thread until the coroutine from coroutineScope will be finished completely. And that's why Coroutine scope is over will always come after Task from nested launch.
From this wonderful article https://jivimberg.io/blog/2018/05/04/parallel-map-in-kotlin/
suspend fun <A, B> Iterable<A>.pmap(f: suspend (A) -> B): List<B> = coroutineScope {
map { async { f(it) } }.awaitAll()
}
With runBlocking, we were not using Structured Concurrency, so an invocation of f could fail and all other executions would continue unfazed. And also we were not playing nice with the rest of the code. By using runBlocking we were forcefully blocking the thread until the whole execution of pmap finishes, instead of letting the caller decide how the execution should go.

Does Kotlin suspend funtion runs on a separate thread?

suspend funtions run on a seperate thread ?
If not, then what is the performance benefit ?
suspend fun requestToken():Token {..} // takes 2 sec to complete
suspend fun createPost (token:Token){..} // takes 3 sec to complete
suspend fun postItem() {
val token = requestToken()
val post =createPost(token)
processPost(post)
}
So, when we reach at processPost(post) and if suspend function do not run on a seperate thread then we have to wait for requestToken() and createPost(token) method
to complete (i.e 2+3= 5 seconds). As per the author, suspend is asyncronous,but if we are not spawning any new thread then how are we achieving asychronous behaviour ?
suspend is asynchronous
suspend funs execute synchronously with their caller. What you actually meant to say is "non-blocking" and that's a completely different story.
but if we are not spawning any new thread then how are we achieving asynchronous behaviour?
You are making the tacit assumption that all I/O must be blocking at some level. This is wrong. Non-blocking I/O works by pushing data to a send buffer and receiving notifications when there's data in the receive buffer. A suspend fun hooks into this mechanism by suspending itself after pushing the data to a send buffer and installing a callback that will resume it when response data is ready in the receive buffer.
Suspension-points can only be used within a coroutine context, for instance:
fun main() {
delay(1000)
}
Would not work because delay is a suspending function and the compiler wouldn't know how to handle that without a coroutine. When you do have a coroutine it can use something called a dispatcher to control thread ownership. Suspending means that the thread is no longer used to execute that part of your program but its doing something else or going idle. The way it works is that you can have several coroutines working at the same time without having a thread for each, that thread can then execute parts of each coroutine up to a suspension point. Generally the idea is that you can view suspending functions as "generators" which have stages for producing a result.
suspend fun hello() {
println("Hello")
delay(1000) // suspend here, let someone else use the thread while we wait
println("World")
delay(1000) // suspend again, we can still use the thread while waiting
println("done")
}
Everytime this is suspended it uses the thread to work on another function until that suspends and the delay expires, at that point this function will eventually resume execution up to the next suspension point or finish execution entirely.
This is different to blocking code as it does not waste the thread by putting it into wait state but rather borrows it to another function. That way no other threads need to be created to have concurrency as you can still work on multiple functions without true parallelism, instead it uses concurrent execution of parts of the functions.
Coroutines don't necessarily protect you from blocking, if you call Thread.sleep(1000) its still gonna be a blocking call. It is the responsibility of the programmer to use suspending equivalents to blocking functions in order to maximize effectiveness of this concept.
For more details, please read the documentation as its very detailed and helpful.
Background single thread or multiple background threads of a thread pool can be explicitly declared and then used, for example by passing it as a parameter, let's call this parameter "scheduler". The really cool thing about it is that initially started from the main thread it's automatically switched to the scheduler thread to execute a particular task on it and virtual machine kind of suspended or interrupted at this place and the thing which is even cooler the main thread gets unblocked and can execute something else while there is the task in background.
As soon as the task is finished, virtual machine sort of gets back to the point where it was suspended or interrupted before and then it resumes its execution from that point but now by also having the result returned from the background thread of the scheduler, below is the code snippet:
private val backgroundThread = ThreadPoolExecutor(1, 1, 15L, TimeUnit.SECONDS, LinkedBlockingQueue())
GlobalScope.launch(Dispatchers.Main, CoroutineStart.DEFAULT) {
postItem(backgroundThread))
}
suspend fun CoroutineScope.postItem(scheduler: ThreadPoolExecutor): Boolean {
val token = requestToken(scheduler)
val post = createPost(token, scheduler)
return processPost(post, scheduler)
}
private suspend fun CoroutineScope.requestToken(scheduler: ThreadPoolExecutor): String {
val def: Deferred<String?> = async(scheduler.asCoroutineDispatcher(), CoroutineStart.DEFAULT) {
val token = networkApi.requestToken()
}
return def.await() ?: ""
}
private suspend fun CoroutineScope.createPost(token: String, scheduler: ThreadPoolExecutor): String {
val def: Deferred<String?> = async(scheduler.asCoroutineDispatcher(), CoroutineStart.DEFAULT) {
val post = networkApi.createPost(token)
}
return def.await() ?: ""
}
private suspend fun CoroutineScope.processPost(post: String, scheduler: ThreadPoolExecutor): Boolean {
val def: Deferred<Boolean?> = async(scheduler.asCoroutineDispatcher(), CoroutineStart.DEFAULT) {
val result = networkApi.processPost(post)
}
return def.await() ?: false
}