$30 off During Our Annual Pro Sale. View Details »

Fresh Async with Kotlin

Fresh Async with Kotlin

Introduction to Kotlin Coroutines for a stable released version with Kotlin 1.3. It covers various async programming styles -- callbacks, futures/promises, and Kotlin suspending functions that are easiest to use of all. It shows integration of Kotlin coroutines with a zoo of futures on JVM, compares them with async/await from other languages, introduces structured concurrency and gives a glimpse of CSP. Presented at GOTO Copenhagen 2018.

Roman Elizarov

November 20, 2018
Tweet

More Decks by Roman Elizarov

Other Decks in Programming

Transcript

  1. Fresh Async with Kotlin
    Roman Elizarov

    View Slide

  2. Speaker: Roman Elizarov
    • Professional developer since 2000
    • Previously developed high-perf trading software
    @ Devexperts
    • Teach concurrent & distributed programming
    @ St. Petersburg ITMO University
    • Chief judge
    @ Northern Eurasia Contest / ACM ICPC
    • Now team lead in Kotlin Libraries
    @ JetBrains

    View Slide

  3. History
    Simula’67
    Coroutines
    CLU’75 Icon’77
    Generators
    C’72 C++’85 Java’95 C#’00 Kotlin’11
    C#’12
    async/await
    Kotlin 1.1 (‘17)
    Coroutines Exp. C++20?
    Java XX?
    Threads
    Asynchronous Programming
    Microservices
    Kotlin 1.3 (‘18)
    Coroutines Release
    Erlang’86 Go’09

    View Slide

  4. Asynchronous Programming with
    Callbacks

    View Slide

  5. Callbacks: before
    fun requestToken(): Token {
    // makes request for a token & waits
    return token // returns result when received
    }
    1

    View Slide

  6. Callbacks: after
    fun requestTokenAsync(cb: (Token) -> Unit) {
    // makes request for a token, invokes callback when done
    // returns immediately
    }
    1
    callback

    View Slide

  7. Callbacks: before
    fun requestTokenAsync(cb: (Token) -> Unit) { … }
    fun createPost(token: Token, item: Item): Post {
    // sends item to the server & waits
    return post // returns resulting post
    }
    2

    View Slide

  8. Callbacks: after
    fun requestTokenAsync(cb: (Token) -> Unit) { … }
    fun createPostAsync(token: Token, item: Item,
    cb: (Post) -> Unit) {
    // sends item to the server, invokes callback when done
    // returns immediately
    }
    2
    callback

    View Slide

  9. Callbacks: before
    fun requestTokenAsync(cb: (Token) -> Unit) { … }
    fun createPostAsync(token: Token, item: Item,
    cb: (Post) -> Unit) { … }
    fun processPost(post: Post) { … }
    fun postItem(item: Item) {
    val token = requestToken()
    val post = createPost(token, item)
    processPost(post)
    }

    View Slide

  10. Callbacks: after
    fun requestTokenAsync(cb: (Token) -> Unit) { … }
    fun createPostAsync(token: Token, item: Item,
    cb: (Post) -> Unit) { … }
    fun processPost(post: Post) { … }
    fun postItem(item: Item) {
    requestTokenAsync { token ->
    createPostAsync(token, item) { post ->
    processPost(post)
    }
    }
    } aka “callback hell”
    This is simplified. Handling
    exceptions makes it a real mess

    View Slide

  11. Futures/Promises
    make it nicer

    View Slide

  12. Futures: before
    fun requestTokenAsync(cb: (Token) -> Unit) {
    // makes request for a token, invokes callback when done
    // returns immediately
    }
    1

    View Slide

  13. Futures: after
    fun requestTokenAsync(): Promise {
    // makes request for a token
    // returns promise for a future result immediately
    }
    1
    future

    View Slide

  14. Futures: before
    fun requestTokenAsync(): Promise { … }
    fun createPostAsync(token: Token, item: Item,
    cb: (Post) -> Unit) {
    // sends item to the server, invokes callback when done
    // returns immediately
    }
    2

    View Slide

  15. Futures: after
    fun requestTokenAsync(): Promise { … }
    fun createPostAsync(token: Token, item: Item): Promise {
    // sends item to the server
    // returns promise for a future result immediately
    }
    future
    2

    View Slide

  16. Futures: before
    fun requestTokenAsync(): Promise { … }
    fun createPostAsync(token: Token, item: Item): Promise …
    fun processPost(post: Post) { … }
    fun postItem(item: Item) {
    requestTokenAsync { token ->
    createPostAsync(token, item) { post ->
    processPost(post)
    }
    }
    }

    View Slide

  17. Futures: after
    fun requestTokenAsync(): Promise { … }
    fun createPostAsync(token: Token, item: Item): Promise …
    fun processPost(post: Post) { … }
    fun postItem(item: Item) {
    requestTokenAsync()
    .thenCompose { token -> createPostAsync(token, item) }
    .thenAccept { post -> processPost(post) }
    }

    View Slide

  18. Futures: after
    fun requestTokenAsync(): Promise { … }
    fun createPostAsync(token: Token, item: Item): Promise …
    fun processPost(post: Post) { … }
    fun postItem(item: Item) {
    requestTokenAsync()
    .thenCompose { token -> createPostAsync(token, item) }
    .thenAccept { post -> processPost(post) }
    }
    Callbacks are still here

    View Slide

  19. Futures: after
    fun requestTokenAsync(): Promise { … }
    fun createPostAsync(token: Token, item: Item): Promise …
    fun processPost(post: Post) { … }
    fun postItem(item: Item) {
    requestTokenAsync()
    .thenCompose { token -> createPostAsync(token, item) }
    .thenAccept { post -> processPost(post) }
    }
    Composable &
    propagates exceptions
    No nesting indentation Callbacks are still here

    View Slide

  20. Futures: after
    fun requestTokenAsync(): Promise { … }
    fun createPostAsync(token: Token, item: Item): Promise …
    fun processPost(post: Post) { … }
    fun postItem(item: Item) {
    requestTokenAsync()
    .thenCompose { token -> createPostAsync(token, item) }
    .thenAccept { post -> processPost(post) }
    }
    But all those operators…

    View Slide

  21. Kotlin coroutines
    to the rescue

    View Slide

  22. Coroutines: before
    fun requestTokenAsync(): Promise {
    // makes request for a token
    // returns promise for a future result immediately
    }
    1

    View Slide

  23. Coroutines: after
    suspend fun requestToken(): Token {
    // makes request for a token & suspends
    return token // returns result when received
    }
    1
    natural signature

    View Slide

  24. Coroutines: before
    suspend fun requestToken(): Token { … }
    fun createPostAsync(token: Token, item: Item): Promise {
    // sends item to the server
    // returns promise for a future result immediately
    }
    2

    View Slide

  25. Coroutines: after
    suspend fun requestToken(): Token { … }
    suspend fun createPost(token: Token, item: Item): Post {
    // sends item to the server & suspends
    return post // returns result when received
    }
    2
    natural signature

    View Slide

  26. Coroutines: before
    suspend fun requestToken(): Token { … }
    suspend fun createPost(token: Token, item: Item): Post { … }
    fun processPost(post: Post) { … }
    fun postItem(item: Item) {
    requestTokenAsync()
    .thenCompose { token -> createPostAsync(token, item) }
    .thenAccept { post -> processPost(post) }
    }

    View Slide

  27. Coroutines: after
    suspend fun requestToken(): Token { … }
    suspend fun createPost(token: Token, item: Item): Post { … }
    fun processPost(post: Post) { … }
    suspend fun postItem(item: Item) {
    val token = requestToken()
    val post = createPost(token, item)
    processPost(post)
    }

    View Slide

  28. Coroutines: after
    suspend fun requestToken(): Token { … }
    suspend fun createPost(token: Token, item: Item): Post { … }
    fun processPost(post: Post) { … }
    suspend fun postItem(item: Item) {
    val token = requestToken()
    val post = createPost(token, item)
    processPost(post)
    }
    Like regular code

    View Slide

  29. Coroutines: after
    suspend fun requestToken(): Token { … }
    suspend fun createPost(token: Token, item: Item): Post { … }
    fun processPost(post: Post) { … }
    suspend fun postItem(item: Item) {
    val token = requestToken()
    val post = createPost(token, item)
    processPost(post)
    }
    suspension
    points

    View Slide

  30. • Regular loops
    Bonus features
    for ((token, item) in list) {
    createPost(token, item)
    }

    View Slide

  31. • Regular exception handing
    Bonus features
    try {
    createPost(token, item)
    } catch (e: BadTokenException) {

    }

    View Slide

  32. • Regular higher-order functions
    Bonus features
    file.readLines().forEach { line ->
    createPost(token, line.toItem())
    }

    View Slide

  33. • Custom higher-order functions
    Bonus features
    val post = retryIO {
    createPost(token, item)
    }
    Everything like in regular code

    View Slide

  34. How does it work?
    A quick peek behind the scenes

    View Slide

  35. Kotlin suspending functions
    callback
    Kotlin
    Java/JVM
    suspend fun createPost(token: Token, item: Item): Post { … }
    Object createPost(Token token, Item item, Continuation cont) { … }

    View Slide

  36. Kotlin suspending functions
    callback
    Kotlin
    Java/JVM
    Continuation is a generic callback interface
    suspend fun createPost(token: Token, item: Item): Post { … }
    Object createPost(Token token, Item item, Continuation cont) { … }
    interface Continuation {
    val context: CoroutineContext
    fun resumeWith(result: Result)
    }

    View Slide

  37. Kotlin suspending functions
    callback
    Kotlin
    Java/JVM
    suspend fun createPost(token: Token, item: Item): Post { … }
    Object createPost(Token token, Item item, Continuation cont) { … }
    interface Continuation {
    val context: CoroutineContext
    fun resumeWith(result: Result)
    }

    View Slide

  38. Kotlin suspending functions
    callback
    Kotlin
    Java/JVM
    suspend fun createPost(token: Token, item: Item): Post { … }
    Object createPost(Token token, Item item, Continuation cont) { … }
    interface Continuation {
    val context: CoroutineContext
    fun resumeWith(result: Result)
    }

    View Slide

  39. Integration
    Zoo of futures on JVM

    View Slide

  40. interface Service {
    fun createPost(token: Token, item: Item): Call
    }
    Retrofit async

    View Slide

  41. interface Service {
    fun createPost(token: Token, item: Item): Call
    }
    suspend fun createPost(token: Token, item: Item): Post =
    serviceInstance.createPost(token, item).await()
    natural signature

    View Slide

  42. interface Service {
    fun createPost(token: Token, item: Item): Call
    }
    suspend fun createPost(token: Token, item: Item): Post =
    serviceInstance.createPost(token, item).await()
    Suspending extension function
    from integration library

    View Slide

  43. suspend fun Call.await(): T {

    }

    View Slide

  44. Callbacks everywhere
    suspend fun Call.await(): T {
    enqueue(object : Callback {
    override fun onResponse(call: Call, response: Response) {
    // todo
    }
    override fun onFailure(call: Call, t: Throwable) {
    // todo
    }
    })
    }

    View Slide

  45. suspend fun Call.await(): T = suspendCoroutine { cont ->
    enqueue(object : Callback {
    override fun onResponse(call: Call, response: Response) {
    if (response.isSuccessful)
    cont.resume(response.body()!!)
    else
    cont.resumeWithException(ErrorResponse(response))
    }
    override fun onFailure(call: Call, t: Throwable) {
    cont.resumeWithException(t)
    }
    })
    }

    View Slide

  46. suspend fun suspendCoroutine(block: (Continuation) -> Unit): T

    View Slide

  47. suspend fun suspendCoroutine(block: (Continuation) -> Unit): T

    View Slide

  48. suspend fun suspendCoroutine(block: (Continuation) -> Unit): T
    Regular function
    Inspired by call/cc from Scheme

    View Slide

  49. suspend fun Call.await(): T = suspendCoroutine { cont ->
    enqueue(object : Callback {
    override fun onResponse(call: Call, response: Response) {
    if (response.isSuccessful)
    cont.resume(response.body()!!)
    else
    cont.resumeWithException(ErrorResponse(response))
    }
    override fun onFailure(call: Call, t: Throwable) {
    cont.resumeWithException(t)
    }
    })
    }

    View Slide

  50. Install callback
    suspend fun Call.await(): T = suspendCoroutine { cont ->
    enqueue(object : Callback {
    override fun onResponse(call: Call, response: Response) {
    if (response.isSuccessful)
    cont.resume(response.body()!!)
    else
    cont.resumeWithException(ErrorResponse(response))
    }
    override fun onFailure(call: Call, t: Throwable) {
    cont.resumeWithException(t)
    }
    })
    }

    View Slide

  51. Install callback
    suspend fun Call.await(): T = suspendCoroutine { cont ->
    enqueue(object : Callback {
    override fun onResponse(call: Call, response: Response) {
    if (response.isSuccessful)
    cont.resume(response.body()!!)
    else
    cont.resumeWithException(ErrorResponse(response))
    }
    override fun onFailure(call: Call, t: Throwable) {
    cont.resumeWithException(t)
    }
    })
    }

    View Slide

  52. Analyze response
    suspend fun Call.await(): T = suspendCoroutine { cont ->
    enqueue(object : Callback {
    override fun onResponse(call: Call, response: Response) {
    if (response.isSuccessful)
    cont.resume(response.body()!!)
    else
    cont.resumeWithException(ErrorResponse(response))
    }
    override fun onFailure(call: Call, t: Throwable) {
    cont.resumeWithException(t)
    }
    })
    }

    View Slide

  53. Analyze response
    suspend fun Call.await(): T = suspendCoroutine { cont ->
    enqueue(object : Callback {
    override fun onResponse(call: Call, response: Response) {
    if (response.isSuccessful)
    cont.resume(response.body()!!)
    else
    cont.resumeWithException(ErrorResponse(response))
    }
    override fun onFailure(call: Call, t: Throwable) {
    cont.resumeWithException(t)
    }
    })
    }
    That’s all

    View Slide

  54. Coroutine builders

    View Slide

  55. Coroutines revisited
    suspend fun requestToken(): Token { … }
    suspend fun createPost(token: Token, item: Item): Post { … }
    fun processPost(post: Post) { … }
    suspend fun postItem(item: Item) {
    val token = requestToken()
    val post = createPost(token, item)
    processPost(post)
    }

    View Slide

  56. Coroutines revisited
    suspend fun requestToken(): Token { … }
    suspend fun createPost(token: Token, item: Item): Post { … }
    fun processPost(post: Post) { … }
    fun postItem(item: Item) {
    val token = requestToken()
    val post = createPost(token, item)
    processPost(post)
    }

    View Slide

  57. Coroutines revisited
    suspend fun requestToken(): Token { … }
    suspend fun createPost(token: Token, item: Item): Post { … }
    fun processPost(post: Post) { … }
    fun postItem(item: Item) {
    val token = requestToken()
    val post = createPost(token, item)
    processPost(post)
    }
    Error: Suspend function 'requestToken' should be called only from
    a coroutine or another suspend function

    View Slide

  58. Coroutines revisited
    suspend fun requestToken(): Token { … }
    suspend fun createPost(token: Token, item: Item): Post { … }
    fun processPost(post: Post) { … }
    fun postItem(item: Item) {
    val token = requestToken()
    val post = createPost(token, item)
    processPost(post)
    }
    Can suspend execution

    View Slide

  59. Coroutines revisited
    suspend fun requestToken(): Token { … }
    suspend fun createPost(token: Token, item: Item): Post { … }
    fun processPost(post: Post) { … }
    fun postItem(item: Item) {
    val token = requestToken()
    val post = createPost(token, item)
    processPost(post)
    }
    Can suspend execution
    A regular function cannot

    View Slide

  60. Coroutines revisited
    suspend fun requestToken(): Token { … }
    suspend fun createPost(token: Token, item: Item): Post { … }
    fun processPost(post: Post) { … }
    fun postItem(item: Item) {
    val token = requestToken()
    val post = createPost(token, item)
    processPost(post)
    }
    Can suspend execution
    A regular function cannot
    One cannot simply invoke a
    suspending function

    View Slide

  61. Launch
    fun postItem(item: Item) {
    GlobalScope.launch {
    val token = requestToken()
    val post = createPost(token, item)
    processPost(post)
    }
    }
    coroutine builder

    View Slide

  62. fun postItem(item: Item) {
    GlobalScope.launch {
    val token = requestToken()
    val post = createPost(token, item)
    processPost(post)
    }
    }
    Fire and forget!

    View Slide

  63. fun postItem(item: Item) {
    GlobalScope.launch {
    val token = requestToken()
    val post = createPost(token, item)
    processPost(post)
    }
    }
    Fire and forget!
    We launch for its
    side effects

    View Slide

  64. fun postItem(item: Item) {
    GlobalScope.launch {
    val token = requestToken()
    val post = createPost(token, item)
    processPost(post)
    }
    }
    Returns immediately, coroutine works
    in background thread pool

    View Slide

  65. fun postItem(item: Item) {
    GlobalScope.launch {
    val token = requestToken()
    val post = createPost(token, item)
    processPost(post)
    }
    }

    View Slide

  66. fun postItem(item: Item) {
    GlobalScope.launch(Dispatchers.Main) {
    val token = requestToken()
    val post = createPost(token, item)
    processPost(post)
    }
    }
    Dispatcher
    Just specify the dispatcher

    View Slide

  67. fun postItem(item: Item) {
    GlobalScope.launch(Dispatchers.Main) {
    val token = requestToken()
    val post = createPost(token, item)
    processPost(post)
    }
    }
    Dispatcher
    And it gets executed on the Main thread

    View Slide

  68. Where’s the magic of launch?

    View Slide

  69. fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext,
    block: suspend () -> Unit
    ): Job { … }
    A regular function

    View Slide

  70. fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext,
    block: suspend () -> Unit
    ): Job { … } suspending lambda

    View Slide

  71. fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext,
    block: suspend () -> Unit
    ): Job { … }

    View Slide

  72. async / await
    The classic approach

    View Slide

  73. Kotlin-way
    suspend fun postItem(item: Item) {
    val token = requestToken()
    val post = createPost(token, item)
    processPost(post)
    }
    Kotlin
    suspend fun requestToken(): Token { … }
    suspend fun createPost(token: Token, item: Item): Post { … }
    fun processPost(post: Post) { … }

    View Slide

  74. async Task postItem(Item item) {
    var token = await requestToken();
    var post = await createPost(token, item);
    processPost(post);
    }
    Classic-way
    C# approach to the same problem (also
    Python, TS, Dart, coming to JS)
    C#
    async Task requestToken() { … }
    async Task createPost(Token token, Item item) { … }
    void processPost(Post post) { … }

    View Slide

  75. async Task postItem(Item item) {
    var token = await requestToken();
    var post = await createPost(token, item);
    processPost(post);
    }
    Classic-way
    mark with async
    C#
    async Task requestToken() { … }
    async Task createPost(Token token, Item item) { … }
    void processPost(Post post) { … }

    View Slide

  76. async Task postItem(Item item) {
    var token = await requestToken();
    var post = await createPost(token, item);
    processPost(post);
    }
    Classic-way
    use await to suspend
    C#
    async Task requestToken() { … }
    async Task createPost(Token token, Item item) { … }
    void processPost(Post post) { … }

    View Slide

  77. async Task postItem(Item item) {
    var token = await requestToken();
    var post = await createPost(token, item);
    processPost(post);
    }
    Classic-way
    C#
    returns a future
    async Task requestToken() { … }
    async Task createPost(Token token, Item item) { … }
    void processPost(Post post) { … }

    View Slide

  78. Why no await keyword in Kotlin?
    The problem with async
    requestToken() VALID –> produces Task
    await requestToken() VALID –> produces Token
    concurrent behavior
    sequential behavior
    C#
    C#
    default

    View Slide

  79. Kotlin suspending functions are
    designed to imitate sequential
    behavior by default
    Concurrency is hard
    Concurrency has to be explicit

    View Slide

  80. Kotlin approach to async
    Concurrency where you need it

    View Slide

  81. Use-case for async
    async Task loadImageAsync(String name) { … }
    C#

    View Slide

  82. Use-case for async
    var promise1 = loadImageAsync(name1);
    var promise2 = loadImageAsync(name2);
    async Task loadImageAsync(String name) { … }
    Start multiple operations
    concurrently
    C#

    View Slide

  83. Use-case for async
    var promise1 = loadImageAsync(name1);
    var promise2 = loadImageAsync(name2);
    var image1 = await promise1;
    var image2 = await promise2;
    async Task loadImageAsync(String name) { … }
    and then wait for them
    C#

    View Slide

  84. Use-case for async
    var result = combineImages(image1, image2);
    C#
    var promise1 = loadImageAsync(name1);
    var promise2 = loadImageAsync(name2);
    var image1 = await promise1;
    var image2 = await promise2;
    async Task loadImageAsync(String name) { … }

    View Slide

  85. Kotlin async function
    fun loadImageAsync(name: String): Deferred =
    GlobalScope.async { … }
    Kotlin

    View Slide

  86. Kotlin async function
    fun loadImageAsync(name: String): Deferred =
    GlobalScope.async { … }
    Kotlin
    A regular function

    View Slide

  87. Kotlin async function
    fun loadImageAsync(name: String): Deferred =
    GlobalScope.async { … }
    Kotlin’s future type
    Kotlin

    View Slide

  88. Kotlin async function
    fun loadImageAsync(name: String): Deferred =
    GlobalScope.async { … }
    async coroutine builder
    Kotlin

    View Slide

  89. fun loadImageAsync(name: String): Deferred =
    GlobalScope.async { … }
    val deferred1 = loadImageAsync(name1)
    val deferred2 = loadImageAsync(name2)
    Start multiple operations
    concurrently
    Kotlin

    View Slide

  90. fun loadImageAsync(name: String): Deferred =
    GlobalScope.async { … }
    val deferred1 = loadImageAsync(name1)
    val deferred2 = loadImageAsync(name2)
    val image1 = deferred1.await()
    val image2 = deferred2.await()
    and then wait for them
    await function
    Suspends until deferred is complete
    Kotlin

    View Slide

  91. fun loadImageAsync(name: String): Deferred =
    GlobalScope.async { … }
    val deferred1 = loadImageAsync(name1)
    val deferred2 = loadImageAsync(name2)
    val image1 = deferred1.await()
    val image2 = deferred2.await()
    val result = combineImages(image1, image2)
    Kotlin
    But what if this crashes?
    Crash?

    View Slide

  92. fun loadImageAsync(name: String): Deferred =
    GlobalScope.async { … }
    val deferred1 = loadImageAsync(name1)
    val deferred2 = loadImageAsync(name2)
    val image1 = deferred1.await()
    val image2 = deferred2.await()
    val result = combineImages(image1, image2)
    Kotlin
    Crash?
    This one leaks
    But what if this crashes?

    View Slide

  93. Idiomatic: Using async function when needed
    suspend fun loadImage(name: String): Image { … }
    Is defined as suspending function, not async

    View Slide

  94. Idiomatic: Using async function when needed
    suspend fun loadImage(name: String): Image { … }
    suspend fun loadAndCombine(name1: String, name2: String): Image =
    coroutineScope {
    val deferred1 = async { loadImage(name1) }
    val deferred2 = async { loadImage(name2) }
    combineImages(deferred1.await(), deferred2.await())
    }
    Structured concurrency

    View Slide

  95. Idiomatic: Using async function when needed
    suspend fun loadImage(name: String): Image { … }
    suspend fun loadAndCombine(name1: String, name2: String): Image =
    coroutineScope {
    val deferred1 = async { loadImage(name1) }
    val deferred2 = async { loadImage(name2) }
    combineImages(deferred1.await(), deferred2.await())
    }

    View Slide

  96. Idiomatic: Using async function when needed
    suspend fun loadImage(name: String): Image { … }
    suspend fun loadAndCombine(name1: String, name2: String): Image =
    coroutineScope {
    val deferred1 = async { loadImage(name1) }
    val deferred2 = async { loadImage(name2) }
    combineImages(deferred1.await(), deferred2.await())
    }

    View Slide

  97. Idiomatic: Using async function when needed
    suspend fun loadImage(name: String): Image { … }
    suspend fun loadAndCombine(name1: String, name2: String): Image =
    coroutineScope {
    val deferred1 = async { loadImage(name1) }
    val deferred2 = async { loadImage(name2) }
    combineImages(deferred1.await(), deferred2.await())
    }

    View Slide

  98. Kotlin approach to async
    requestToken() VALID –> produces Token
    async { requestToken() } VALID –> produces Deferred
    sequential behavior
    concurrent behavior
    Kotlin
    Kotlin
    default

    View Slide

  99. Structured concurrency

    View Slide

  100. Structured concurrency
    suspend fun loadImage(name: String): Image { … }
    suspend fun loadAndCombine(name1: String, name2: String): Image =
    coroutineScope {
    val deferred1 = async { loadImage(name1) }
    val deferred2 = async { loadImage(name2) }
    combineImages(deferred1.await(), deferred2.await())
    }

    View Slide

  101. Crash
    suspend fun loadImage(name: String): Image { … }
    suspend fun loadAndCombine(name1: String, name2: String): Image =
    coroutineScope {
    val deferred1 = async { loadImage(name1) }
    val deferred2 = async { loadImage(name2) }
    combineImages(deferred1.await(), deferred2.await())
    }
    Crashes?

    View Slide

  102. Cancels scope
    suspend fun loadImage(name: String): Image { … }
    suspend fun loadAndCombine(name1: String, name2: String): Image =
    coroutineScope {
    val deferred1 = async { loadImage(name1) }
    val deferred2 = async { loadImage(name2) }
    combineImages(deferred1.await(), deferred2.await())
    }
    Cancels

    View Slide

  103. Cancels children
    suspend fun loadImage(name: String): Image { … }
    suspend fun loadAndCombine(name1: String, name2: String): Image =
    coroutineScope {
    val deferred1 = async { loadImage(name1) }
    val deferred2 = async { loadImage(name2) }
    combineImages(deferred1.await(), deferred2.await())
    }
    Cancels

    View Slide

  104. Kotlin suspending functions
    may use concurrency internally,
    but don’t leak it
    Concurrency needs to be structured

    View Slide

  105. Parallelism is optional

    View Slide

  106. Beyond asynchronous code
    Kotlin’s approach to generate/yield – synchronous coroutines

    View Slide

  107. Fibonacci sequence
    val fibonacci: Sequence = …

    View Slide

  108. Fibonacci sequence
    val fibonacci = sequence {
    var cur = 1
    var next = 1
    while (true) {
    yield(cur)
    val tmp = cur + next
    cur = next
    next = tmp
    }
    }

    View Slide

  109. Fibonacci sequence
    val fibonacci = sequence {
    var cur = 1
    var next = 1
    while (true) {
    yield(cur)
    val tmp = cur + next
    cur = next
    next = tmp
    }
    }
    println(fibonacci.take(10).toList())

    View Slide

  110. Fibonacci sequence
    val fibonacci = sequence {
    var cur = 1
    var next = 1
    while (true) {
    yield(cur)
    val tmp = cur + next
    cur = next
    next = tmp
    }
    }
    println(fibonacci.take(10).toList())
    >> [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

    View Slide

  111. Fibonacci sequence
    val fibonacci = sequence {
    var cur = 1
    var next = 1
    while (true) {
    yield(cur)
    val tmp = cur + next
    cur = next
    next = tmp
    }
    }
    A coroutine builder with
    restricted suspension

    View Slide

  112. Fibonacci sequence
    val fibonacci = sequence {
    var cur = 1
    var next = 1
    while (true) {
    yield(cur)
    val tmp = cur + next
    cur = next
    next = tmp
    }
    }
    A suspending function in
    the scope of buildSequence

    View Slide

  113. Fibonacci sequence
    val fibonacci = sequence {
    var cur = 1
    var next = 1
    while (true) {
    yield(cur)
    val tmp = cur + next
    cur = next
    next = tmp
    }
    }
    println(fibonacci.take(10).toList())
    Synchronous

    View Slide

  114. Fibonacci sequence
    val fibonacci = sequence {
    var cur = 1
    var next = 1
    while (true) {
    yield(cur)
    val tmp = cur + next
    cur = next
    next = tmp
    }
    }
    val iter = fibonacci.iterator()

    View Slide

  115. Fibonacci sequence
    val fibonacci = sequence {
    var cur = 1
    var next = 1
    while (true) {
    yield(cur)
    val tmp = cur + next
    cur = next
    next = tmp
    }
    }
    val iter = fibonacci.iterator()
    println(iter.next())

    View Slide

  116. Fibonacci sequence
    val fibonacci = sequence {
    var cur = 1
    var next = 1
    while (true) {
    yield(cur)
    val tmp = cur + next
    cur = next
    next = tmp
    }
    }
    val iter = fibonacci.iterator()
    println(iter.next())

    View Slide

  117. Fibonacci sequence
    val fibonacci = sequence {
    var cur = 1
    var next = 1
    while (true) {
    yield(cur)
    val tmp = cur + next
    cur = next
    next = tmp
    }
    }
    val iter = fibonacci.iterator()
    println(iter.next())

    View Slide

  118. Fibonacci sequence
    val fibonacci = sequence {
    var cur = 1
    var next = 1
    while (true) {
    yield(cur)
    val tmp = cur + next
    cur = next
    next = tmp
    }
    }
    val iter = fibonacci.iterator()
    println(iter.next())

    View Slide

  119. Fibonacci sequence
    val fibonacci = sequence {
    var cur = 1
    var next = 1
    while (true) {
    yield(cur)
    val tmp = cur + next
    cur = next
    next = tmp
    }
    }
    val iter = fibonacci.iterator()
    println(iter.next()) // 1

    View Slide

  120. Fibonacci sequence
    val fibonacci = sequence {
    var cur = 1
    var next = 1
    while (true) {
    yield(cur)
    val tmp = cur + next
    cur = next
    next = tmp
    }
    }
    val iter = fibonacci.iterator()
    println(iter.next()) // 1
    println(iter.next())

    View Slide

  121. Fibonacci sequence
    val fibonacci = sequence {
    var cur = 1
    var next = 1
    while (true) {
    yield(cur)
    val tmp = cur + next
    cur = next
    next = tmp
    }
    }
    val iter = fibonacci.iterator()
    println(iter.next()) // 1
    println(iter.next())

    View Slide

  122. Fibonacci sequence
    val fibonacci = sequence {
    var cur = 1
    var next = 1
    while (true) {
    yield(cur)
    val tmp = cur + next
    cur = next
    next = tmp
    }
    }
    val iter = fibonacci.iterator()
    println(iter.next()) // 1
    println(iter.next())

    View Slide

  123. Fibonacci sequence
    val fibonacci = sequence {
    var cur = 1
    var next = 1
    while (true) {
    yield(cur)
    val tmp = cur + next
    cur = next
    next = tmp
    }
    }
    val iter = fibonacci.iterator()
    println(iter.next()) // 1
    println(iter.next())

    View Slide

  124. Fibonacci sequence
    val fibonacci = sequence {
    var cur = 1
    var next = 1
    while (true) {
    yield(cur)
    val tmp = cur + next
    cur = next
    next = tmp
    }
    }
    val iter = fibonacci.iterator()
    println(iter.next()) // 1
    println(iter.next())

    View Slide

  125. Fibonacci sequence
    val fibonacci = sequence {
    var cur = 1
    var next = 1
    while (true) {
    yield(cur)
    val tmp = cur + next
    cur = next
    next = tmp
    }
    }
    val iter = fibonacci.iterator()
    println(iter.next()) // 1
    println(iter.next()) // 1

    View Slide

  126. Fibonacci sequence
    val fibonacci = sequence {
    var cur = 1
    var next = 1
    while (true) {
    yield(cur)
    val tmp = cur + next
    cur = next
    next = tmp
    }
    }
    val iter = fibonacci.iterator()
    println(iter.next()) // 1
    println(iter.next()) // 1 etc ad infinum

    View Slide

  127. Sharing state?

    View Slide

  128. Shared Mutable State
    Operations
    Operations
    Operations

    View Slide

  129. Shared Mutable State
    Operations
    Operations
    Operations
    Needs
    synchronization

    View Slide

  130. Shared Mutable State
    @stefanobaghino

    View Slide

  131. Communicating
    Sequential
    Processes

    View Slide

  132. Concurrent Fibonacci sequence
    val fibonacci = GlobalScope.produce {
    var cur = 1
    var next = 1
    while (true) {
    send(cur)
    val tmp = cur + next
    cur = next
    next = tmp
    }
    }
    Asynchronous
    (can be in different thread)

    View Slide

  133. Concurrent Fibonacci sequence
    val fibonacci = GlobalScope.produce {
    var cur = 1
    var next = 1
    while (true) {
    send(cur)
    val tmp = cur + next
    cur = next
    next = tmp
    }
    }
    fun main(args: Array) = runBlocking {
    println(fibonacci.receive())
    }
    Another coroutine
    Asynchronous
    (can be in different thread)

    View Slide

  134. Concurrent Fibonacci sequence
    val fibonacci = GlobalScope.produce {
    var cur = 1
    var next = 1
    while (true) {
    send(cur)
    val tmp = cur + next
    cur = next
    next = tmp
    }
    }
    fun main(args: Array) = runBlocking {
    println(fibonacci.receive())
    }
    Another coroutine
    Receives from channel
    Asynchronous
    (can be in different thread)

    View Slide

  135. Concurrent Fibonacci sequence
    val fibonacci = GlobalScope.produce {
    var cur = 1
    var next = 1
    while (true) {
    send(cur)
    val tmp = cur + next
    cur = next
    next = tmp
    }
    }
    fun main(args: Array) = runBlocking {
    println(fibonacci.receive()) // 1
    }

    View Slide

  136. Concurrent Fibonacci sequence
    val fibonacci = GlobalScope.produce {
    var cur = 1
    var next = 1
    while (true) {
    send(cur)
    val tmp = cur + next
    cur = next
    next = tmp
    }
    }
    fun main(args: Array) = runBlocking {
    println(fibonacci.receive()) // 1
    println(fibonacci.receive()) // 1
    }
    etc ad infinum

    View Slide

  137. Library vs Language
    Keeping the core language small

    View Slide

  138. Classic async
    async/await
    generate/yield
    Keywords

    View Slide

  139. Kotlin coroutines
    suspend Modifier

    View Slide

  140. Kotlin coroutines
    Standard
    library

    View Slide

  141. Kotlin coroutines
    Standard
    library
    kotlinx-coroutines
    launch, async, runBlocking,
    Job, Deferred, etc
    http://github.com/kotlin/kotlinx.coroutines

    View Slide

  142. View Slide

  143. Thank you
    Any questions?
    elizarov @
    Roman Elizarov
    relizarov

    View Slide

  144. View Slide