Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

KotlinConf Keynote Recap

Slide 4

Slide 4 text

KotlinConf Keynote Recap

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

suspending function

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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を介した コールバックに変換される

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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; } }

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

Thanks!