Kotlin coroutine delay waits for couroutine completion/fire and forget - kotlin

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.

Related

launch long-running task then immediately send HTTP response

Using ktor HTTP server, I would like to launch a long-running task and immediately return a message to the calling client. The task is self-sufficient, it's capable of updating its status in a db, and a separate HTTP call returns its status (i.e. for a progress bar).
What I cannot seem to do is just launch the task in the background and respond. All my attempts at responding wait for the long-running task to complete. I have experimented with many configurations of runBlocking and coroutineScope but none are working for me.
// ktor route
get("/launchlongtask") {
val text: String = (myFunction(call.request.queryParameters["loops"]!!.toInt()))
println("myFunction returned")
call.respondText(text)
}
// in reality, this function is complex... the caller (route) is not able to
// determine the response string, it must be done here
suspend fun myFunction(loops : Int) : String {
runBlocking {
launch {
// long-running task, I want to launch it and move on
(1..loops).forEach {
println("this is loop $it")
delay(2000L)
// updates status in db here
}
}
println("returning")
// this string must be calculated in this function (or a sub-function)
return#runBlocking "we just launched $loops loops"
}
return "never get here" // actually we do get here in a coroutineScope
}
output:
returning
this is loop 1
this is loop 2
this is loop 3
this is loop 4
myFunction returned
expected:
returning
myFunction returned
(response sent)
this is loop 1
this is loop 2
this is loop 3
this is loop 4
Just to explain the issue with the code in your question, the problem is using runBlocking. This is meant as the bridge between the synchronous world and the async world of coroutines and
"the name of runBlocking means that the thread that runs it ... gets blocked for the duration of the call, until all the coroutines inside runBlocking { ... } complete their execution."
(from the Coroutine docs).
So in your first example, myFunction won't complete until your coroutine containing loop completes.
The correct approach is what you do in your answer, using CoroutineScope to launch your long-running task. One thing to point out is that you are just passing in a Job() as the CoroutineContext parameter to the CoroutineScope constructor. The CoroutineContext contains multiple things; Job, CoroutineDispatcher, CoroutineExceptionHandler... In this case, because you don't specifiy a CoroutineDispatcher it will use CoroutineDispatcher.Default. This is intended for CPU-intensive tasks and will be limited to "the number of CPU cores (with a minimum of 2)". This may or may not be want you want. An alternative is CoroutineDispatcher.IO - which has a default of 64 threads.
inspired by this answer by Lucas Milotich, I utilized CoroutineScope(Job()) and it seems to work:
suspend fun myFunction(loops : Int) : String {
CoroutineScope(Job()).launch {
// long-running task, I want to launch it and move on
(1..loops).forEach {
println("this is loop $it")
delay(2000L)
// updates status in db here
}
}
println("returning")
return "we just launched $loops loops"
}
not sure if this is resource-efficient, or the preferred way to go, but I don't see a whole lot of other documentation on the topic.

Kotlin coroutines - async withTimeout which stops blocking the thread once it runs out of time

I'm trying to make a function which triggers a possibly slow operation which can't be cancelled. I want this operation to run in a coroutine with a timeout. Since the operation cannot be cancelled as mentioned before, I need the function to return after the time out, but the operation to stay in the background.
The code I have been trying to get working runs a lengthy operation of 10 seconds asynchronously which has a time out of 5 seconds, so therefore the function should return after the timeout and let main continue its job, printing "foo execution finished", and finally 5 more seconds later the slow job would print "job ends (10 seconds passed)".
Here's the code:
fun main() {
println("program execution begins")
foo()
println("foo execution finished")
while(true);
}
fun foo() = runBlocking {
val job = async {
val endTimeMillis = System.currentTimeMillis() + (10 * 1000)
while (System.currentTimeMillis() <= endTimeMillis); //blocks for 10 seconds
println("job ends (10 seconds passed)")
}
try {
withTimeout(5000) {
println("start awaiting with 5 secs timeout")
job.await()
}
} catch (ex: TimeoutCancellationException) {
println("out of time")
}
}
Which then produces the following result:
program execution begins
start awaiting with 5 secs timeout
job ends (10 seconds passed)
out of time
foo execution finished
But this is not exactly the behavior I need in this case as mentioned before. I need to make it so that output looks something like:
program execution begins
start awaiting with 5 secs timeout
out of time
foo execution finished
job ends (10 seconds passed)
In addition to this, I can't use any sort of "kotlin-coroutines" function in the async to archive this behavior (well, cooperate with the cancellation), since the code called in there will be user code unrelated to the coroutine, possibly written in Java. hence the while loop for blocking the async block instead of a delay() in the sample.
Thanks in advance for the help!
If you can't interrupt the blocking code, then you'll need to run it in a different thread. Otherwise your thread will have no opportunity whatsoever to process the timeout.
Also you need to make sure that the Job that contains the blocking code is not a child of your waiting Job. Otherwise the timeout will cancel the blocking Job, but it will still spin for 10 seconds, and runBlocking will wait for it to finish.
The easiest way to do both of these things is to use GlobalScope, like this:
fun foo() = runBlocking {
try {
withTimeout(5000) {
println("start awaiting with 5 secs timeout")
GlobalScope.async {
val endTimeMillis = System.currentTimeMillis() + (10 * 1000)
while (System.currentTimeMillis() <= endTimeMillis); //blocks for 10 seconds
println("job ends (10 seconds passed)")
}.await()
}
} catch (ex: TimeoutCancellationException) {
println("out of time")
}
}
Of course, that thread is going to be spinning for 10 seconds even after you stop waiting for it... which is awful, so I hope you really have a good reason to want this.
Something like this should work:
fun main() {
println("start")
foo()
println("foo finished")
while (true);
}
fun foo() {
val start = System.currentTimeMillis()
GlobalScope.launch {
val endTimeMillis = System.currentTimeMillis() + (10 * 1000)
while (System.currentTimeMillis() <= endTimeMillis); //blocks for 10 seconds
println("${start.secondsSince()} job ends (10 seconds passed)")
}
println("${start.secondsSince()} waiting")
runBlocking {
delay(5000)
}
println("${start.secondsSince()} finished waiting")
}
fun Long.secondsSince() = (System.currentTimeMillis() - this) / 1000.00
This will output:
start
0.04 waiting
5.049 finished waiting
foo finished
10.043 job ends (10 seconds passed)
Ignore the horrible way of counting seconds please, it is 2am, but it does prove the point.
Now to why this works. Firstly I'd recommend reading this SO question on the difference between async and launch. TLDR launch is used for fire and forget, since you are not interested in any result (no return value needed), there is no need to use async. Secondly the docs on delay.
The reason that yours did not work is (from what I can tell)
You were using withTimeout on the job.await, which is not really the same as running it on the coroutine itself.
The foo fun was blocking, that means that it will always wait for all the coroutines that are launched from inside it, before it continues. This means that you would have never gotten the foo execution finished before job ends (10 seconds passed).
Btw, the actual final code would just be:
fun foo() {
GlobalScope.launch {
//code that takes a long time
}
runBlocking { delay(5000) }
//other code that will continue after 5 seconds
}
While I have already marked an accepted answer, it is worth noting that my solution ended up being a bit different. [for future generations reading this :), hello future!]
From what #Alex.T pointed out:
The foo fun was blocking, that means that it will always wait for all the coroutines that are launched from inside it, before it continues. This means that you would have never gotten the foo execution finished before job ends (10 seconds passed).
With this in mind, I came out with the following solution:
fun main() {
println("program execution begins")
foo()
println("foo execution finished")
while(true);
}
fun foo() {
val job = GlobalScope.launch {
val endTimeMillis = System.currentTimeMillis() + 10000
while (System.currentTimeMillis() <= endTimeMillis);
println("job ends (10 seconds passed)")
}
runBlocking {
try {
withTimeout(5000) {
println("start awaiting with 5 secs timeout")
job.join()
}
} catch (ex: TimeoutCancellationException) {
println("out of time")
job.cancel()
}
}
}
Which doesn't wrap both coroutines with runBlocking, only the withTimeout. So our actual blocking operation runs separately in global scope, and therefore, it produces the following output
program execution begins
start awaiting with 5 secs timeout
out of time
foo execution finished
job ends (10 seconds passed)
(The actual output I wanted in the question, wow!)
It's also worth pointing out here what #Matt Timmermans said:
Of course, that thread is going to be spinning for 10 seconds even after you stop waiting for it... which is awful, so I hope you really have a good reason to want this.
So use this with caution.

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.

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.

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
}