I'm trying to understand kotlin coroutines, I'm coming from C# and there's something I'm not understanding here in kotlin. In this scenario I'm writing a webapi using Kotlin in the Quarkus framework. From what I can tell if I label a controller (or resource) function as a suspend function quarkus will automatically launch it in a coroutine.
The issue i have is i don't know what the preferred method for suspending that coroutine is. The vast majority of examples I see on kotlin coroutines use the delay() function, which internally uses suspendCancellableCoroutine() to suspend the function. That makes sense, but i don't see a lot of example calling suspendCancellableCoroutine() explicitly. I've done some reading about the underlying code that gets generated in a suspend function, and some resources lead me to believe that by virtue of calling another suspend function i'll hit a suspend point and that will suspend my coroutine. In C# i'd usually just call await() from inside my async function to execute the long running code.
In my kotlin setup i have setup an instance of jmeter and i simulate 5 threads calling my API at the same time, while limiting my program to run on a single thread in quarkus. My API then makes a call to another API (i'll call that API, data API from now on), which could be a long running operation. For the purpose of my test my data API has a 1 second sleep in it.
Essentially:
web api controller -> web api processing -> web api calls data api through client -> data API does slow operation
I've tried calling async/await on the call to the data API, which seems to work, JMeter reports that 5 requests are all completed in roughly 1 second, and the logging i have indicates that all 5 requests are handled on a single thread. This feels clunky though. I'm already in a coroutine and now my coroutine is creating a new coroutine (async is a coroutine builder) to execute the long running function.
I've also removed the async/await and updated the call to the data API to be a suspend function as well (though this is a client generated from resteasy client). This also seems to work, but resteasy reactive could be generating something that's doing the suspend for me. I need to work with a simpler example, but in the mean time...
If i'm not using the delay() function in Kotlin, and i'm executing code in a coroutine, what is the preferred method to indicate that a section of code is potentially blocking and my coroutine should be suspended? Do i launch a new coroutine? Call suspendCancellableCoroutine()? Or something else? Probably overthinking this, but i want to make sure i understand this.
The coroutines library provides several suspend functions you can use to suspend in a coroutine or in another suspend function, among them:
withContext
delay
coroutineScope
supervisorScope
suspendCoroutine
suspendCancellableCoroutine
Job.join
Deferred.await
The typical way to convert blocking (long-running synchronous) code into something you can use in a coroutine is to wrap it in withContext(Dispatchers.Default) { } or withContext(Dispatchers.IO) { }. If it's something you use repeatedly, you can write a suspend function for it:
suspend fun foo() = withContext(Dispatchers.IO) {
blockingFoo()
}
but if it's some one-off blocking chunk of code, you can use withContext directly in a coroutine.
Note, using async { }.await() is basically never done. The compiler warns you against it. You should be using withContext instead. Calling await on a Deferred is used either when one coroutine needs a result from some other coroutine that has been passed to it, or when you're working with multiple parallel children coroutines inside a coroutineScope block.
The typical way to convert asynchronous callback-based code into a suspend function so you can use it synchronously in a coroutine is to use suspendCoroutine or suspendCancellableCoroutine. You can look up how to use those. They are pretty low level. Many libraries like Retrofit and Firebase already provide suspend functions you can use instead of the callbacks.
coroutineScope and supervisorScope are for creating a scope inside your coroutine to run multiple children coroutines in parallel and wait for them all.
Related
I have a personal project written in Kotlin, and I developed a habit of using withContext(...) very generously. I tend to use withContext(Dispatchers.IO) when calling anything that could possibly be related to I/O.
For example:
suspend fun getSomethingFromDatabase(db: AppDatabase) = withContext(Dispatchers.IO) {
return // ...
}
suspend fun doSomethingWithDatabaseItem(db: AppDatabase) {
val item = withContext(Dispatchers.IO) {
getSomethingFromDatabase(db)
}
// ...
}
You can see a redundant withContext(Dispatchers.IO) in the second function. I'm being extra cautious here, because I might not know/remember if getSomethingFromDatabase switches to an appropriate context or not. Does this impact performance? Is this bad? What's the idiomatic way of dealing with Dispatchers?
Note: I know that it's perfectly fine to switch between different contexts this way, but this question is specifically about using the same context.
You do not need withContext for anything besides calling code that demands a specific context. Therefore withContext(Dispatchers.Main) should only be used when you're working with UI functions that require the main thread. And you should only use withContext(Dispatchers.IO) when calling blocking IO related code.
A proper suspend function does not block (see Suspending convention section here), and therefore, you should never have to specify a dispatcher to call a suspend function. The exception would be if you're working with someone else's code or API and they are using suspend functions incorrectly!
I don't know what your AppDatabase class is, but if it is sensibly designed, it will expose suspend functions instead of blocking functions, so you should not need withContext to retrieve values from it. But if it does expose blocking functions for retrieving items, then the code of your first function is correct.
And your second function definitely doesn't need withContext because it's simply using it to call something that I can see is a suspend function.
As for whether it's OK to use redundant context switching...it doesn't hurt anything besides possibly wasting a tiny bit of time and memory context switching and allocating lambdas for no reason. And it makes your code less readable.
I'm learning Coroutines of Kotlin. The Text A is from https://codelabs.developers.google.com/codelabs/kotlin-coroutines/#8
What does main-safe in Kotlin Coroutines?
Does it mean the system will run them in background thread automatically when it need ?
Does it mean I will never use the code such as withContext(Dispatchers.IO) in both Room and Retrofit operation ?
Text A
Both Room and Retrofit make suspending functions main-safe.
It's safe to call these suspend funs from Dispatchers.Main, even though they fetch from the network and write to the database.
For me as an android developer this simple definition made perfect sens
We consider a function main-safe when it doesn't block UI updates on
the main thread.
Found it here https://developer.android.com/kotlin/coroutines
What does main-safe [mean for] Kotlin Coroutines?
You literally quote the answer yourself:
It's safe to call these suspend funs from Dispatchers.Main, even though they fetch from the network and write to the database.
And the answer to
Does it mean I will never use the code such as withContext(Dispatchers.IO) in both Room and Retrofit operation ?
is: Correct (assuming you configure them properly, e.g. use suspend modifier in Retrofit fun definitions).
Please check this answer for the exact reason why the API calls works with retrofit without changing the dispatcher to IO, referencing https://stackoverflow.com/a/61216333/4354001 :
It works because Retrofit's suspend implementation delegates to
Call.enqueue. This means it already executes on its own background
executor by default instead of using the caller's Dispatcher.
Let's assume I'm writing a library that returns a string which is a complex and long running task.
I can chose between offering this
interface StringGenerator {
suspend fun generateString(): String
}
or
interface StringGenerator {
fun generateString(): Deferred<String>
}
Are there any (dis-)advantages of either of the options and which are they? Which should I choose?
Kotlin coroutines are designed along the "sequential by default" guideline. That means that your API should always expose suspend funs and the user, if and when they really need it, can easily wrap them in async.
The advantage of that is analogous to the advantages of cold flows with respect to hot flows: a suspendable function is active only while control is inside it. When it returns, it has not left behind a task running in the background.
Whenever you return a Deferred, the user must start worrying what happens if they don't manage to await on the result. Some code paths may ignore it, the calling code may get an exception, and then their application has a leak.
I've been reading up about concurrency in Kotlin and thought I started to understand it... Then I discovered that async() has been deprecated in 1.3 and I'm back to the start.
Here's what I'd like to do: create a thread (and it does have to be a thread rather than a managed pool, unfortunately), and then be able to execute async blocks on that thread, and return Deferred instances that will let me use .await().
What is the recommended way to do this in Kotlin?
1. Single-threaded coroutine dispatcher
Here's what I'd like to do: create a thread (and it does have to be a thread rather than a managed pool, unfortunately)
Starting a raw thread to handle your coroutines is an option only if you're prepared to dive deep and implement your own coroutine dispatcher for that case. Kotlin offers support for your requirement via a single-threaded executor service wrapped into a dispatcher. Note that this still leaves you with almost complete control over how you start the thread, if you use the overload that takes a thread factory:
val threadPool = Executors.newSingleThreadExecutor {
task -> Thread(task, "my-background-thread")
}.asCoroutineDispatcher()
2. async-await vs. withContext
and then be able to execute async blocks on that thread, and return Deferred instances that will let me use .await().
Make sure you actually need async-await, which means you need it for something else than
val result = async(singleThread) { blockingCal() }.await()
Use async-await only if you need to launch a background task, do some more stuff on the calling thread, and only then await() on it.
Most users new to coroutines latch onto this mechanism due to its familiarity from other languages and use it for plain sequential code like above, but avoiding the pitfall of blocking the UI thread. Kotlin has a "sequential by default" philosophy which means you should instead use
val result = withContext(singleThread) { blockingCall() }
This doesn't launch a new coroutine in the background thread, but transfers the execution of the current coroutine onto it and back when it's done.
3. Deprecated top-level async
Then I discovered that async() has been deprecated in 1.3
Spawning free-running background tasks is a generally unsound practice because it doesn't behave well in the case of errors or even just unusual patterns of execution. Your calling method may return or fail without awaiting on its result, but the background task will go on. If the application repeatedly re-enters the code that spawns the background task, your singleThread executor's queue will grow without bound. All these tasks will run without a purpose because their requestor is long gone.
This is why Kotlin has deprecated top-level coroutine builders and now you must explicitly qualify them with a coroutine scope whose lifetime you must define according to your use case. When the scope's lifetime runs out, it will automatically cancel all the coroutines spawned within it.
On the example of Android this would amount to binding the coroutine scope to the lifetime of an Activity, as explained in the KDoc of CoroutineScope.
Like it's stated with the message, it's deprecated in favor of calling async with an explicit scope like GlobalScope.async {} instead.
This is the actual implementation of the deprecated method as well.
By removing the top level async function, you'll not run into issues with implicit scopes or wrong imports.
Let me recommend this solution: Kotlin coroutines with returned value
It parallelizes tasks into 3 background threads (so called "triplets pool") but it's easy to change it to be single threaded as per your requirement by replacing tripletsPool with backgroundThread as below:
private val backgroundThread = ThreadPoolExecutor(1, 1, 5L, TimeUnit.SECONDS, LinkedBlockingQueue())
I'm starting with coroutines in Android app. I'm rewriting callbacks to suspendCoroutine<> {} and I've got one dillema: when should I just mark the function as suspend, and when should I wrap the call in some builder (launch, async, etc.)?
Is there some best practice, rule of thumb, or something?
You should write a suspend fun for every asynchronous, callback-based API call you're currently making.
You should wrap in withContext(myThreadPool) every synchronous API call you're making.
All Android-friendly APIs that do I/O use the async approach, so for these you'll be writing suspend funs, but for CPU-intensive tasks (such as decoding images) you may need withContext.
Finally, to be able to use either kind of calls, you must create a top-level coroutine with launch(UI).
Keep in mind that the above is really just a rule of thumb. When you factor your code, many times you realize you need, for example, a suspend fun to make a CPU-intensive operation because there's a withContext somewhere on that call path.
Let me also add a warning about a very typical misuse of the coroutine APIs: you almost never need async-await. Use it only for the cases where you want to truly run it "in the background" while you continue to perform other stuff in your current context. In simpler terms, you should never write
val result = async { calculation() }.await()
Instead you should write
val result = withContext(myThreadPool) { calculation() }