Kotlin coroutines overview
Last week I have to refresh my knowledge about kotlin coroutines and this publication is it's overview.
Where it comes from
Originally appeared in 1960x in COBOL&Simula. Threads were introduced a bit later and market preferred them.
They have been using quite a long time until one its issue become more and more disturbing.
Threads are quite expensive mechanism (in resources terms), there is a limit how much threads can be run at the same time.
When demand in scalability is growing this limit becomes an issue.
Market introduced async/await mechanism as an attempt to solve this issue. Coroutines were next attempt.
Why do we need it
When you run a thread to do some work in the background sometimes you need to get back result of this work. Common practice is to pass ref on Callback
and call it from the thread. This approach leads to rising amount of callbacks calls which is known as 'callbacks hell'.
Future/Promises/Deffered were a response on this issue, but they brought a new one -- writing async code becomes a new paradigm and you have to combine it
with paradigm of writing non async code.
Coroutines were introduced as a solution for that. It allows you to do block of code in parallel, it is lightweight and allows you write async and non async code in the same style.
How does it work
Kotlin extends a signature of a method by passing Continuation function as a parameter and adding 'suspend' modifier to the method.
inline fun'Continuation' in the official documentationContinuation( context: CoroutineContext, crossinline resumeWith: (Result ) -> Unit ): Continuation
Continuation function wraps call on next suspend function. The call of this continuation object is optional and might be not called at all.
Then compiler transforms all suspend functions into state machine and through continuation object traverse over it. Function behind continuation object might be called in same or new thread which brings additional flexibility. Here is continuation object passing into all methods is the same which is valuable optimisation compare with others solutions, e.g. Promises.
Kotlin code transformed into state machine.
How does it integrate existing codebase
It does over integration library and extension await()
. await() transforms Future (e.g. Call
suspendCoroutine() is that what stops a method and wraps block of executing code
suspend inline fun
suspendCoroutine() in the official documentation
Traditional and suspend functions have different nature, it implies they would not be chained without some mediator. Coroutine builder act as
this mediator (e.g. launch {} block). In the nutshell it is a suspend wrapper around all block of code and, sometimes, mechanism that run parallelism.
Execution context (CoroutineContext) defines thread to run in, e.g. launch(UI) {} runs the last method in block in the UI thread
Coroutine context in the official documentation
Worth to note
Access&manipulation on shared resource can be done new and reinvented kotlin mechanisms: thread confinement, mutex and actors
val counterContext = newSingleThreadContext("CounterContext") var counter = 0 fun main() = runBlocking { // confine everything to a single-threaded context withContext(counterContext) { massiveRun { counter++ } } println("Counter = $counter") }More informatiion in official docs Do not run function without access to the scope. If you need to launch a coroutine that keeps running after your function returns, then make your function an extension of CoroutineScope or pass scope: CoroutineScope as parameter to make your intent clear in your function signature. Do not make these functions suspending: [3]
fun CoroutineScope.doThis() { launch { println("I'm fine") } } fun doThatIn(scope: CoroutineScope) { scope.launch { println("I'm fine, too") } }
Materials used:
[1] Roman Elizarov's intro into coroutines
[2] Origins of coroutines
[3] Proper use of coroutine scope