Friday, April 30, 2021

Rules of Structured concurrency in Kotlin: Part I — Philosophical

It’s interesting, that in comparison to Java Kotlin has a high-level feature — structural concurrency. This feature is closer to domain, architecture, and lifecycle of UI and it helps not to reinvent the wheel, of course, if this tool is clear. 

After reading the documentation, and articles of Roman Elizarov and Sean McQuillan I found that there are no clear rules of structured concurrency. 

So let’s try to create them in two steps — first, we summarise what structured concurrency is, and second, we give rules and examples of using. 

⇒⇒⇒

 “We can use structured concurrency in our code. Instead of launching coroutines in the GlobalScope, just like we usually do with threads (threads are always global), we can launch coroutines in the specific scope of the operation we are performing” 

⇒⇒⇒

 “The mechanism providing the structure of the coroutines is called "structured concurrency". Let's see what benefits structured concurrency has over global scopes:
  • The scope is generally responsible for child coroutines, and their lifetime is attached to the lifetime of the scope.
  • The scope can automatically cancel child coroutines if something goes wrong or if a user simply changes their mind and decides to revoke the operation.
  • The scope automatically waits for completion of all the child coroutines. Therefore, if the scope corresponds to a coroutine, then the parent coroutine does not complete until all the coroutines launched in its scope are complete.” 

⇒⇒⇒

 “Structured concurrency is a combination of language features and best practices that, when followed, help you keep track of all work running in coroutines.
On Android, we can use structured concurrency to do three things:
  1. Cancel work when it is no longer needed.
  2. Keep track of work while it’s running.
  3. Signal errors when a coroutine fails.
Structured concurrency guarantees that when a suspend function returns, all of its work is done.
Structured concurrency guarantees that when a coroutine errors, its caller or scope is notified.
  1. When a scope cancels, all of its coroutines cancel.
  2. When a suspend fun returns, all of its work is done.
  3. When a coroutine errors, its caller or scope is notified.”

⇒⇒⇒

“The concept structured concurrency has more philosophy behind it. 

Asynchronous operations can take long, long time to complete if there is some problem with a network or with a back end. Moreover, these operations are usually executed in the scope of some UI element like a window or a page. If an operation takes too long to complete, a typical user closes the corresponding UI element and goes do something else or, which is worse, reopens this UI and tries the operation again and again. But we have the previous operation still running in background and we need some mechanism to cancel it when the corresponding UI element is closed by a user. In Kotlin coroutines this had lead us to recommend quite tricky design patterns one has to follow in their code to ensure this cancellation is handled properly. 

On a more philosophical level, you rarely launch coroutines “globally”, like you do with threads.” 

⇒⇒⇒

So see you in next article — part II, with rules of Structured concurrency