Upgrade to Pro — share decks privately, control downloads, hide ads and more …

suspending functionの裏側

suspending functionの裏側

第07回Kotlin勉強会@Sansanでの発表資料です。

Keita Kagurazaka

November 09, 2017
Tweet

More Decks by Keita Kagurazaka

Other Decks in Programming

Transcript

  1. suspending functionの裏側
    2017/11/9
    第7回Kotlin勉強会@Sansan
    @kkagurazaka

    View full-size slide

  2. 皆さん、coroutines使ってますか?

    View full-size slide

  3. KotlinConf Keynote Recap

    View full-size slide

  4. KotlinConf Keynote Recap

    View full-size slide

  5. coroutines
    じゃんじゃん使っていきましょう

    View full-size slide

  6. そんなcoroutinesが
    どうやって実現されているか
    その裏側をお話します

    View full-size slide

  7. 自己紹介
    ● Keita Kagurazaka
    ● Android App Developer
    ● Sansan株式会社 2017/4〜
    ● Kotlin / CQRS / DDD /
    Splatoon / Github: @k-kagurazaka
    Twitter: @kkagurazaka

    View full-size slide

  8. coroutinesとは
    ● 中断・再開可能な計算のインスタンス
    ○ Threadクラスのように、作成して実行する
    launch(UI) {
    val token = fetchToken() // Tokenが取得できるまで実行を中断
    val item = postItem(token, newItem) // 投稿が完了するまで実行を中断
    updateUI(item)
    }

    View full-size slide

  9. どのように中断・再開を
    実現しているのか?

    View full-size slide

  10. suspending function

    View full-size slide

  11. suspending function
    ● 呼び出されるとcoroutinesを中断する(こともできる)
    ● suspending functionはcoroutinesかsuspending functionか
    らしか呼び出せない
    ● Java byte codeになる際にCPS transformされる

    View full-size slide

  12. suspending function
    ● 呼び出されるとcoroutinesを中断する(こともできる)
    ● suspending functionはcoroutinesかsuspending functionか
    らしか呼び出せない
    ● Java byte codeになる際にCPS transformされる

    View full-size slide

  13. Continuation Passing Style (CPS)
    戻り値で値を返すのではなく、引数のContinuationインスタンスに
    結果を渡すことで値を伝達するスタイル
    interface Continuation {
    // 成功したことと、その結果を伝達
    fun resume(value: T): Unit
    //失敗したことと、原因の例外を伝達
    fun resumeWithException(exception: Throwable): Unit
    }

    View full-size slide

  14. CPS transform
    // Kotlinでの宣言
    suspend fun fetchToken(): Token
    // 変換後のJava byte codeをKotlinっぽく書いたもの
    fun fetchToken(continuation: Continuation): Any?
    ● 中断する場合は COROUTINE_SUSPENDED 定数を返し、
    再開するときにContinuationをresumeする
    ● 中断しない場合は結果の T を返す

    View full-size slide

  15. CPS transform後のイメージ
    launch(UI) {
    fetchToken(object : Continuation {
    override fun resume(value: Token) {
    postItem(value, newItem, object: Continuation {
    override fun resume(value: Item) {
    updateUI(value)
    }
    })
    }
    })
    }
    suspending functionの呼び出し以降がContinuationを介した
    コールバックに変換される

    View full-size slide

  16. 内部的な実装とはいえ
    コールバックのネストが
    深すぎないか?

    View full-size slide

  17. ステートマシンによる実装
    class StateMachine implements Continuation {
    int label = 0; // ステートマシンの状態
    Token token; // suspending functionのローカル変数
    Item item; // 同上
    void resume(Object data) { … }
    }

    View full-size slide

  18. void resume(Object data) {
    switch(label) {
    case 0:
    label = 1;
    data = fetchToken(this); // CPS変換後なのでContinuation=thisを渡す
    if (data == COROUTINE_SUSPENDED) break; // 中断が発生した場合は抜ける
    case 1:
    token = (Token)data;
    label = 2;
    data = postItem(token, newItem, this);
    if (data == COROUTINE_SUSPENDED) break;
    case 2:
    item = (Item)data;
    updateUI(item)
    label = -1; // ステートマシンの終了
    break;
    }
    }

    View full-size slide

  19. 内部実装がコールバックヘル
    というわけではない!

    View full-size slide

  20. まとめ
    ● coroutineの中断はsuspending functionで発生する
    ● suspending functionはCPS transformによってコールバック
    に変換される
    ● 複数のsuspending functionの呼び出しはステートマシンにコ
    ンパイルされる

    View full-size slide