suspending functionの裏側

suspending functionの裏側

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

0f8842fabcd31a6c9007ddcd648247db?s=128

Keita Kagurazaka

November 09, 2017
Tweet

Transcript

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

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

  3. KotlinConf Keynote Recap

  4. KotlinConf Keynote Recap

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

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

  8. 自己紹介 • Keita Kagurazaka • Android App Developer • Sansan株式会社

    2017/4〜 • Kotlin / CQRS / DDD / Splatoon / Github: @k-kagurazaka Twitter: @kkagurazaka
  9. coroutinesとは • 中断・再開可能な計算のインスタンス ◦ Threadクラスのように、作成して実行する launch(UI) { val token =

    fetchToken() // Tokenが取得できるまで実行を中断 val item = postItem(token, newItem) // 投稿が完了するまで実行を中断 updateUI(item) }
  10. どのように中断・再開を 実現しているのか?

  11. suspending function

  12. suspending function • 呼び出されるとcoroutinesを中断する(こともできる) • suspending functionはcoroutinesかsuspending functionか らしか呼び出せない •

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

    Java byte codeになる際にCPS transformされる
  14. Continuation Passing Style (CPS) 戻り値で値を返すのではなく、引数のContinuationインスタンスに 結果を渡すことで値を伝達するスタイル interface Continuation<in T> {

    // 成功したことと、その結果を伝達 fun resume(value: T): Unit //失敗したことと、原因の例外を伝達 fun resumeWithException(exception: Throwable): Unit }
  15. CPS transform // Kotlinでの宣言 suspend fun fetchToken(): Token // 変換後のJava

    byte codeをKotlinっぽく書いたもの fun fetchToken(continuation: Continuation<Token>): Any? • 中断する場合は COROUTINE_SUSPENDED 定数を返し、 再開するときにContinuationをresumeする • 中断しない場合は結果の T を返す
  16. CPS transform後のイメージ launch(UI) { fetchToken(object : Continuation<Token> { override fun

    resume(value: Token) { postItem(value, newItem, object: Continuation<Item> { override fun resume(value: Item) { updateUI(value) } }) } }) } suspending functionの呼び出し以降がContinuationを介した コールバックに変換される
  17. 内部的な実装とはいえ コールバックのネストが 深すぎないか?

  18. ステートマシンによる実装 class StateMachine implements Continuation<Object> { int label = 0;

    // ステートマシンの状態 Token token; // suspending functionのローカル変数 Item item; // 同上 void resume(Object data) { … } }
  19. 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; } }
  20. 内部実装がコールバックヘル というわけではない!

  21. まとめ • coroutineの中断はsuspending functionで発生する • suspending functionはCPS transformによってコールバック に変換される •

    複数のsuspending functionの呼び出しはステートマシンにコ ンパイルされる
  22. Thanks!