Kotlin Fest 2025 - 内部実装から理解する Coroutines ― Continuation・Structured Concurrency・Dispatcher
by
kaseken (Kent Kaseda)
Link
Embed
Share
Beginning
This slide
Copy link URL
Copy link URL
Copy iframe embed code
Copy iframe embed code
Copy javascript embed code
Copy javascript embed code
Share
Tweet
Share
Tweet
Slide 1
Slide 1 text
内部実装から理解するCoroutines Continuation‧Structured Concurrency‧Dispatcher Kotlin Fest 2025 @kentkaseda kaseken kaseken
Slide 2
Slide 2 text
はじめに スライド: https://speakerdeck.com/kaseken/kotlin-fest-2025-nei-bu-shi-zhuang-karali-jie-suru-coroutines-c ontinuationstructured-concurrencydispatcher 原稿: https://zenn.dev/kaseken/articles/681c5aea0639c7
Slide 3
Slide 3 text
本発表のゴール 1. Kotlin Coroutinesの中⼼的なコンセプトを “仕組みから” 理解していただく 2. Kotlin Coroutinesのソースコードを気軽に読めるようになっていただく
Slide 4
Slide 4 text
Kotlin Coroutinesの中⼼的なコンセプト 1. Continuation suspend関数は、どのように⼀時停⽌‧再開しているのか? 2. Dispatcher コルーチンは、どのように⾮同期処理‧並⾏処理を実⾏するのか? 3. Structured Concurrency Structured Concurrencyとは何か。そして、どのように実現されているのか?
Slide 5
Slide 5 text
1. Continuation suspend関数は、どのように⼀時停⽌‧再開しているのか? 2. Dispatcher コルーチンは、どのように⾮同期処理‧並⾏処理を実⾏するのか? 3. Structured Concurrency Structured Concurrencyとは何か。そして、どのように実現されているのか? Kotlin Coroutinesの中⼼的なコンセプト
Slide 6
Slide 6 text
suspend関数の仕様 「中断」と「再開」がどのように実現されているのかを内部実装から追う delayが呼ばれた後「中断」する delayが完了した後「再開」する https://pl.kotl.in/awpNrOZhv
Slide 7
Slide 7 text
Kotlin Coroutinesの内部実装の追い⽅ 1. JetBrains/kotlin1)のソースコードを読む 言語レベルのコア機能 (Continuation等) の仕組みを知りたいとき 2. Kotlin/kotlinx.coroutines2)のソースコードを読む 拡張ライブラリの機能 (Coroutine Builder・CoroutineScope・Dispatcher等) の仕組み を知りたいとき 3. コンパイル後のバイトコードをデコンパイルしたコードを読む コンパイラの仕組みを知りたいとき 1) https://github.com/JetBrains/kotlin 2) https://github.com/Kotlin/kotlinx.coroutines
Slide 8
Slide 8 text
suspend関数の内部実装の追い⽅ 1. JetBrains/kotlin1)のソースコードを読む 言語レベルのコア機能 (Continuation等) の仕組みを知りたいとき 2. Kotlin/kotlinx.coroutines2)のソースコードを読む 拡張ライブラリの機能 (Coroutine Builder・CoroutineScope・Dispatcher等) の仕組み を知りたいとき 3. コンパイル後のバイトコードをデコンパイルしたコードを読む コンパイラの仕組みを知りたいとき 1) https://github.com/JetBrains/kotlin 2) https://github.com/Kotlin/kotlinx.coroutines
Slide 9
Slide 9 text
suspend関数のバイトコードを表⽰ IntelliJ IDEAの「Tools > Kotlin > Show Kotlin Bytecode」でBytecodeを表⽰
Slide 10
Slide 10 text
「Decompile」を実⾏すると Javaコードが復元される suspend関数のバイトコードをデコンパイル
Slide 11
Slide 11 text
suspend関数のバイトコードをデコンパイル ① 関数定義 ② Continuationの初期化 ③ suspend関数の実⾏
Slide 12
Slide 12 text
セクション① 関数定義 suspend関数の引数に「Continuation」が追加されている suspend関数は、HelloWorldKtクラスのstatic関数として定義される コンパイラによるバイトコードへの変換 デコンパイルによるJavaコードの復元
Slide 13
Slide 13 text
Continuationとは何か? ● 各suspend関数は「Continuation」を持つ ● Continuationの「resumeWith」を呼ぶと、対応するsuspend関数が再開する https://github.com/JetBrains/kotlin/blob/0075ea767c181d7a3b3b5c123a0d15fcc278e447/libraries/stdlib/src/kotlin/coroutines/Continuation.kt#L16 Continuationとは「中断されたsuspend関数を再開するためのハンドラ」
Slide 14
Slide 14 text
Continuationの役割をサンプルコードで解説 main関数 mainのContinuation 覚えてほしいポイント ● 各suspend関数はContinuationを持つ ● ContinuationのresumeWithを呼ぶと 対応するsuspend関数が再開する
Slide 15
Slide 15 text
main関数 helloWorld関数 mainのContinuation mainのContinuationが引数として渡される helloWorldの Continuation 覚えてほしいポイント ● 各suspend関数はContinuationを持つ ● ContinuationのresumeWithを呼ぶと 対応するsuspend関数が再開する Continuationの役割をサンプルコードで解説
Slide 16
Slide 16 text
main関数 helloWorld関数 mainのContinuation helloWorldのContinuationが引数として渡される delay関数 helloWorldの Continuation 覚えてほしいポイント ● 各suspend関数はContinuationを持つ ● ContinuationのresumeWithを呼ぶと 対応するsuspend関数が再開する Continuationの役割をサンプルコードで解説
Slide 17
Slide 17 text
main関数 helloWorld関数 mainのContinuation delay関数 helloWorldの Continuation helloWorldのContinuationのresumeWithを呼ぶと helloWorld関数が再開する 覚えてほしいポイント ● 各suspend関数はContinuationを持つ ● ContinuationのresumeWithを呼ぶと 対応するsuspend関数が再開する Continuationの役割をサンプルコードで解説
Slide 18
Slide 18 text
main関数 helloWorld関数 mainのContinuation helloWorldの Continuation mainのContinuationのresumeWithを呼ぶと main関数が再開する 覚えてほしいポイント ● 各suspend関数はContinuationを持つ ● ContinuationのresumeWithを呼ぶと 対応するsuspend関数が再開する Continuationの役割をサンプルコードで解説
Slide 19
Slide 19 text
関数の引数に追加されたContinuationとは? 引数の「Continuation $completion」とは 呼び出し元の suspend関数に対応する Continuationである! …と言い切れれば単純だったが、実際にはこれは半分正解・半分不正解
Slide 20
Slide 20 text
セクション② suspend関数に紐づくContinuationの初期化
Slide 21
Slide 21 text
suspend関数の初回呼び出し時 呼び出し元のContinuationが渡される suspend関数に紐づくContinuationが初期化される
Slide 22
Slide 22 text
Continuationの初期化 invokeSuspendは再開時に呼ばれる すなわち、再開時にもHelloWorldKt.helloWorldが呼ばれる 再開時には、この関数のContinuationが渡される 「label」は、この関数がどこで再開すべきかを保持
Slide 23
Slide 23 text
invokeSuspendが再開時に呼ばれることの確認 https://github.com/JetBrains/kotlin/blob/0075ea767c181d7a3b3b5c123a0d15fcc278e447/libraries/stdlib/jvm/src/kotlin/coroutines/jvm/internal/ContinuationImpl.kt#L16 再開時に呼ばれるresumeWith内で invokeSuspendが呼ばれる 覚えてほしいポイント (再掲) ● 各suspend関数はContinuationを持つ ● ContinuationのresumeWithを呼ぶと 対応するsuspend関数が再開する
Slide 24
Slide 24 text
suspend関数の再開時 この関数のContinuationが渡される (初回実行時は “呼び出し元” のContinuationが渡される) Continuationの持つ「label」が取得される
Slide 25
Slide 25 text
セクション③ suspend関数の実⾏
Slide 26
Slide 26 text
セクション③ suspend関数の実⾏:初回実⾏時 元の関数 ここまで実⾏ labelが1に変更
Slide 27
Slide 27 text
セクション③ suspend関数の実⾏:再開時 元の関数 ここから実⾏
Slide 28
Slide 28 text
まとめ:Continuationによるsuspend関数の中断‧再開の仕組み helloWorld関数 mainの Continuation main関数
Slide 29
Slide 29 text
まとめ:Continuationによるsuspend関数の中断‧再開の仕組み helloWorld関数 mainの Continuation main関数 helloWorldの Continuationを初期化
Slide 30
Slide 30 text
まとめ:Continuationによるsuspend関数の中断‧再開の仕組み helloWorld関数 mainの Continuation main関数 helloWorldの Continuationを初期化 label: 0の範囲のコードを実⾏ labelを1に変更 delay関数 helloWorldの Continuation 中断
Slide 31
Slide 31 text
まとめ:Continuationによるsuspend関数の中断‧再開の仕組み helloWorld関数 mainの Continuation main関数 helloWorldの Continuationを初期化 label: 0の範囲のコードを実⾏ labelを1に変更 delay関数 helloWorldの Continuation 中断 🕰 1秒経過 helloWorldのContinuationの resumeWithが呼ばれる 再開
Slide 32
Slide 32 text
まとめ:Continuationによるsuspend関数の中断‧再開の仕組み helloWorld関数 mainの Continuation main関数 helloWorldの Continuationを初期化 label: 0の範囲のコードを実⾏ labelを1に変更 delay関数 helloWorldの Continuation 中断 🕰 1秒経過 再開 label: 1の範囲のコードを実⾏ helloWorldのContinuationの resumeWithが呼ばれる mainのContinuationの resumeWithが呼ばれる
Slide 33
Slide 33 text
Kotlin Coroutinesの中⼼的なコンセプト 1. Continuation suspend関数は、どうやって⼀時停⽌‧再開しているのか? 2. Dispatcher コルーチンは、どのように⾮同期処理‧並⾏処理を実⾏するのか? 3. Structured Concurrency Structured Concurrencyとは何か。そして、どのように実現されているのか?
Slide 34
Slide 34 text
Dispatcherの表⾯的な仕様 (Dispatchers.Default) ● Coroutine Builder (launch) に引数を指定しない場合、Dispatchers.Default (CPU-boundなタスク⽤) が利⽤され、バックグラウンドで⾮同期実⾏される ○ ID: 12, 13のバックグラウンドスレッドが使⽤されている https://pl.kotl.in/67I3RBx0v
Slide 35
Slide 35 text
Dispatcherの表⾯的な仕様 (Dispatchers.IO) https://pl.kotl.in/67I3RBx0v ● Coroutine Builder (launch) の引数に指定することで、Dispatchers.IO (IO-boundなタスク⽤) を利⽤できる ○ ID: 12, 13, 14, 15, 17のスレッドが使⽤されている
Slide 36
Slide 36 text
Dispatcherの内部実装の追い⽅ 1. JetBrains/kotlin1)のソースコードを読む 言語レベルのコア機能 (Continuation等) の仕組みを知りたいとき 2. Kotlin/kotlinx.coroutines2)のソースコードを読む 拡張ライブラリの機能 (Coroutine Builder・CoroutineScope・Dispatcher等) の仕組み を知りたいとき 3. コンパイル後のバイトコードをデコンパイルしたコードを読む コンパイラの仕組みを知りたいとき 1) https://github.com/JetBrains/kotlin 2) https://github.com/Kotlin/kotlinx.coroutines
Slide 37
Slide 37 text
launchのソースコードを読む https://github.com/Kotlin/kotlinx.coroutines/blob/8c27d51025d56a7b8de8eec2fb234b0094ce84f1/kotlinx-coroutines-core/common/src/Builders.common.kt#L44 Coroutine Builderの代表例として「launch」のソースコードを読む
Slide 38
Slide 38 text
launchのソースコードを読む https://github.com/Kotlin/kotlinx.coroutines/blob/8c27d51025d56a7b8de8eec2fb234b0094ce84f1/kotlinx-coroutines-core/common/src/Builders.common.kt#L44 コルーチンに紐づくCoroutineContext (= コルーチンに紐づくメタデータのコンテナ ) の初期化 Dispatcherも、CoroutineContextの一要素
Slide 39
Slide 39 text
launchのソースコードを読む https://github.com/Kotlin/kotlinx.coroutines/blob/8c27d51025d56a7b8de8eec2fb234b0094ce84f1/kotlinx-coroutines-core/common/src/Builders.common.kt#L44 コルーチン (StandaloneCoroutine) のインスタンスが初期化される
Slide 40
Slide 40 text
launchのソースコードを読む https://github.com/Kotlin/kotlinx.coroutines/blob/8c27d51025d56a7b8de8eec2fb234b0094ce84f1/kotlinx-coroutines-core/common/src/Builders.common.kt#L44 StandaloneCoroutine.startが呼ばれコルーチンが起動
Slide 41
Slide 41 text
AbstractCoroutine.startのソースコードを読む https://github.com/Kotlin/kotlinx.coroutines/blob/8c27d51025d56a7b8de8eec2fb234b0094ce84f1/kotlinx-coroutines-core/common/src/AbstractCoroutine.kt#L133 https://github.com/Kotlin/kotlinx.coroutines/blob/8c27d51025d56a7b8de8eec2fb234b0094ce84f1/kotlinx-coroutines-core/common/src/CoroutineStart.kt#L356 AbstractCoroutine.kt CoroutineStart.kt CoroutineStartのinvokeが呼ばれる startCoroutineCancellableが呼ばれる Cancellable.kt https://github.com/Kotlin/kotlinx.coroutines/blob/8c27d51025d56a7b8de8eec2fb234b0094ce84f1/kotlinx-coroutines-core/common/src/intrinsics/Cancellable.kt#L23
Slide 42
Slide 42 text
startCoroutineCancellableのソースコードを読む https://github.com/Kotlin/kotlinx.coroutines/blob/8c27d51025d56a7b8de8eec2fb234b0094ce84f1/kotlinx-coroutines-core/common/src/intrinsics/Cancellable.kt#L23
Slide 43
Slide 43 text
startCoroutineCancellableのソースコードを読む コルーチンに紐づく Continuation createCoroutineIntercepted https://github.com/Kotlin/kotlinx.coroutines/blob/8c27d51025d56a7b8de8eec2fb234b0094ce84f1/kotlinx-coroutines-core/common/src/intrinsics/Cancellable.kt#L23 コルーチンに紐づくContinuation が初期化される
Slide 44
Slide 44 text
startCoroutineCancellableのソースコードを読む コルーチンに紐づく Continuation createCoroutineIntercepted https://github.com/Kotlin/kotlinx.coroutines/blob/8c27d51025d56a7b8de8eec2fb234b0094ce84f1/kotlinx-coroutines-core/common/src/intrinsics/Cancellable.kt#L23 intercepted() コルーチンに紐づく Continuation DispatchedContinuation ContinuationがDispatchedContinuation でwrapされる
Slide 45
Slide 45 text
startCoroutineCancellableのソースコードを読む コルーチンに紐づく Continuation createCoroutineIntercepted https://github.com/Kotlin/kotlinx.coroutines/blob/8c27d51025d56a7b8de8eec2fb234b0094ce84f1/kotlinx-coroutines-core/common/src/intrinsics/Cancellable.kt#L23 intercepted() コルーチンに紐づく Continuation DispatchedContinuation resumeCancellableWith() Dispatcher DispatchedContinuationがdispatchされる
Slide 46
Slide 46 text
startCoroutineCancellableのソースコードを読む コルーチンに紐づく Continuation createCoroutineIntercepted intercepted() コルーチンに紐づく Continuation DispatchedContinuation resumeCancellableWith() Dispatcher DispatchedContinuationがdispatchされる wrapされたContinuationのresumeWithを呼ぶ (= コルーチンの実行 ) https://github.com/Kotlin/kotlinx.coroutines/blob/8c27d51025d56a7b8de8eec2fb234b0094ce84f1/kotlinx-coroutines-core/common/src/intrinsics/Cancellable.kt#L23
Slide 47
Slide 47 text
interceptedのソースコード https://github.com/JetBrains/kotlin/blob/15b95af041cb7068f1a721278f7a5c0057fbfa4e/libraries/stdlib/jvm/src/kotlin/coroutines/jvm/internal/ContinuationImpl.kt#L111 https://github.com/Kotlin/kotlinx.coroutines/blob/f4f519b36734238ec686dfaec1e174086691781e/kotlinx-coroutines-core/common/src/CoroutineDispatcher.kt#L240 CoroutineContextからCoroutineDispatcherが取り出され、interceptContinuationが呼ばれる ContinuationがDispatchedContinuationでwrapされる
Slide 48
Slide 48 text
resumeCancellableWithのソースコード https://github.com/Kotlin/kotlinx.coroutines/blob/8c27d51025d56a7b8de8eec2fb234b0094ce84f1/kotlinx-coroutines-core/common/src/internal/DispatchedContinuation.kt#L206C1-L219C6 CoroutineDispatcher.safeDispatchが呼ばれる CoroutineDispatcher.dispatchが呼ばれる https://github.com/Kotlin/kotlinx.coroutines/blob/8c27d51025d56a7b8de8eec2fb234b0094ce84f1/kotlinx-coroutines-core/common/src/internal/DispatchedContinuation.kt#L252
Slide 49
Slide 49 text
DispatchedTask.runのソースコード https://github.com/Kotlin/kotlinx.coroutines/blob/8c27d51025d56a7b8de8eec2fb234b0094ce84f1/kotlinx-coroutines-core/common/src/internal/DispatchedTask.kt#L77 DispatchedTaskのrunが呼ばれる (DispatchedContinuationの基底クラス) Continuation.resumeが呼ばれる (resume内でresumeWithが呼ばれる(ref))
Slide 50
Slide 50 text
Dispatcherの内部実装を追う コルーチンに紐づく Continuation createCoroutineIntercepted intercepted() コルーチンに紐づく Continuation DispatchedContinuation resumeCancellableWith() Dispatcher DispatchedContinuationがdispatchされる wrapされたContinuationのresumeWithを呼ぶ (= コルーチンの実行 ) launch
Slide 51
Slide 51 text
CoroutineDispatcherの種別 ● Dispatchers.Default CPU-Boundな処理の実⾏に使⽤ ● Dispatchers.IO IO-Boundな処理の実⾏に使⽤ (Dispatchers.Defaultと同じスレッドプールを利⽤) ● Dispatchers.Main メインスレッド (UIスレッド) で実⾏する CoroutineDispatcherの実装は、実⾏環境 (JVM/Native/Web) ごとに異なる Dispatchers.DefaultおよびDispatchers.IOのJVMにおける内部実装を調査
Slide 52
Slide 52 text
CoroutineDispatcher.dispatchが呼ばれた後の流れ Dispatchers.Default.dispatch (Ref) CoroutineScheduler.dispatch (Ref) Dispatchers.IO.dispatch (Ref) LimitedDispatchers.dispatch (Ref) SchedulerCoroutineDispatcher.dispatch (Ref) CoroutineScheduler.dispatchの実装は複雑なため、模式図を用いて解説する (⚠ここからの説明は簡略化したもので、実態とは異なります )
Slide 53
Slide 53 text
Dispatchers.Defaultでコルーチンが実⾏される仕組み CoroutineScheduler TaskQueue (CPU-bound) TaskQueue (IO-bound) Dispatchers.Defaultに コルーチンをdispatch CoroutineSchedulerのCPU-boundなタスク⽤のキューに追加される Thread Pool
Slide 54
Slide 54 text
CoroutineScheduler TaskQueue (CPU-bound) TaskQueue (IO-bound) Dispatchers.Defaultに コルーチンをdispatch タスクを処理するためのWorker (JavaのThreadの⼦クラス (Ref)) が作られる Worker (待機中) Dispatchers.Defaultでコルーチンが実⾏される仕組み Thread Pool
Slide 55
Slide 55 text
タスクの取得・実行 をループする CoroutineScheduler TaskQueue (CPU-bound) TaskQueue (IO-bound) Dispatchers.Defaultに コルーチンをdispatch Workerが起動され、タスクの取得‧コルーチン実⾏ (DispatchedTask.run) をループする Worker (タスク実行中) Dispatchers.Defaultでコルーチンが実⾏される仕組み Thread Pool
Slide 56
Slide 56 text
CoroutineScheduler TaskQueue (CPU-bound) TaskQueue (IO-bound) Dispatchers.Defaultに コルーチンをdispatch CPU-boundなタスクを処理中のWorkerの個数は、CPUのコア数以下に制限される (= 上限に達している場合には、dispatch時にWorkerの作成は⾏われない) Worker (タスク実行中) Worker (タスク実行中) Dispatchers.Defaultでコルーチンが実⾏される仕組み Thread Pool
Slide 57
Slide 57 text
Dispatchers.IOでコルーチンが実⾏される仕組み CoroutineScheduler TaskQueue (CPU-bound) TaskQueue (IO-bound) Dispatchers.IOに コルーチンをdispatch Worker (タスク実行中) Worker (タスク実行中) CoroutineSchedulerのIO-boundなタスク⽤のキューに追加される Thread Pool
Slide 58
Slide 58 text
CoroutineScheduler TaskQueue (CPU-bound) TaskQueue (IO-bound) Dispatchers.IOに コルーチンをdispatch Worker (タスク実行中) Worker (タスク実行中) Worker (待機中) Dispatchers.IOでコルーチンが実⾏される仕組み Thread Pool タスクを処理するためのWorker (JavaのThreadの⼦クラス (Ref)) が作られる
Slide 59
Slide 59 text
CoroutineScheduler TaskQueue (CPU-bound) TaskQueue (IO-bound) Dispatchers.IOに コルーチンをdispatch Worker (タスク実行中) Worker (タスク実行中) Worker (タスク実行中) タスクの取得・実行 をループする Dispatchers.IOでコルーチンが実⾏される仕組み Thread Pool Workerが起動され、タスクの取得‧コルーチン実⾏ (DispatchedTask.run) をループする (※Dispatchers.Defaultと同じスレッドプールを利⽤)
Slide 60
Slide 60 text
CoroutineScheduler TaskQueue (CPU-bound) TaskQueue (IO-bound) Dispatchers.IOに コルーチンをdispatch Worker (タスク実行中) Worker (タスク実行中) Worker (タスク実行中) Worker (タスク実行中) IO-boundなタスクを処理中のWorkerの個数には上限がない (※ ただし、実質的には上限がある。前段でlimitedParallelismによる制限がある) Worker (タスク実行中) Dispatchers.IOでコルーチンが実⾏される仕組み Thread Pool
Slide 61
Slide 61 text
IO-boundなタスクを処理中のWorkerの個数には上限がない (※ ただし、実質的には上限がある。前段でlimitedParallelismによる制限がある) CoroutineScheduler TaskQueue (CPU-bound) TaskQueue (IO-bound) Dispatchers.IOに コルーチンをdispatch Worker (タスク実行中) Worker (タスク実行中) Worker (タスク実行中) タスクの取得・実行 をループする Dispatchers.IOでコルーチンが実⾏される仕組み Thread Pool ⚠ ここまでの説明は簡略化したもので、実態とは異なります 実際には、Workerごとのローカルのタスクキューもあるなど、もっと複雑です 正確な実装を知りたい⽅は、CoroutineSchedulerのソースコード (Ref) を読んでください
Slide 62
Slide 62 text
limitedParallelismによる並列に実⾏されるコルーチン数の制限 limitedParallelism(2) limitedParallelism(10) https://pl.kotl.in/V1x5RYgiI
Slide 63
Slide 63 text
CoroutineDispatcher.dispatchが呼ばれた後の流れ (再掲) Dispatchers.Default.dispatch (Ref) CoroutineScheduler.dispatch (Ref) Dispatchers.IO.dispatch (Ref) LimitedDispatchers.dispatch (Ref) SchedulerCoroutineDispatcher.dispatch (Ref) CoroutineScheduler.dispatchの実装は複雑なため、模式図を用いて解説する (⚠ここからの説明は簡略化したもので、実態とは異なります ) limitedParallelismを指定すると LimitedDispatcherを介するようになる (Dispatchers.IOはデフォルトで.limitedParallelism(64)が適⽤
Slide 64
Slide 64 text
LimitedDispatcherの内部実装 CoroutineScheduler TaskQueue (CPU-bound) TaskQueue (IO-bound) Worker (タスク実行中) Worker (タスク実行中) Worker (タスク実行中) Worker (タスク実行中) Worker (タスク実行中) TaskQueue LimitedDispatcher Worker Worker Worker Dispatchers.IOに コルーチンをdispatch 前段のLimitedDispatcherで 実⾏中のコルーチン数が指定した並列数以下になるよう制限される 並列数を超える場合 このキューで待機 Workerの個数を 並列数以下に制御 Ref) https://github.com/Kotlin/kotlinx.coroutines/blob/8c27d51025d56a7b8de8eec2fb234b0094ce84f1/kotlinx-coroutines-core/common/src/internal/LimitedDispatcher.kt#L22
Slide 65
Slide 65 text
1. Continuation suspend関数は、どうやって⼀時停⽌‧再開しているのか? 2. Dispatcher コルーチンは、どのように⾮同期処理‧並⾏処理を実⾏するのか? 3. Structured Concurrency Structured Concurrencyとは何か。そして、どのように実現されているのか? Kotlin Coroutinesの中⼼的なコンセプト
Slide 66
Slide 66 text
Structured Concurrencyとは Parent Coroutine Child Coroutine ⼦コルーチンが完了するまで親コルーチンが完了しないこと
Slide 67
Slide 67 text
Structured Concurrencyとは Parent Coroutine Child Coroutine Structured Concurrencyに反する場合、リソースリークのリスクがある Kotlin Coroutinesでは (⼀般的な⽤法において) Structured Concurrencyが守られる ⚠リソースリークのリスク
Slide 68
Slide 68 text
Kotlin CoroutinesにおけるStructured Concurrency ● CoroutineScopeは、⼦コルーチンが全て完了してからスコープを抜ける ● 全てのコルーチンは、CoroutineScopeを起点に起動する必要がある ○ Coroutine Builder (launchやasync) はCoroutineScopeの拡張関数 Kotlin Coroutinesでは (⼀般的な⽤法において) Structured Concurrencyが守られる
Slide 69
Slide 69 text
CoroutineScopeの内部実装の追い⽅ 1. JetBrains/kotlin1)のソースコードを読む 言語レベルのコア機能 (Continuation等) の仕組みを知りたいとき 2. Kotlin/kotlinx.coroutines2)のソースコードを読む 拡張ライブラリの機能 (Coroutine Builder・CoroutineScope・Dispatcher等) の仕組み を知りたいとき 3. コンパイル後のバイトコードをデコンパイルしたコードを読む コンパイラの仕組みを知りたいとき 1) https://github.com/JetBrains/kotlin 2) https://github.com/Kotlin/kotlinx.coroutines
Slide 70
Slide 70 text
coroutineScopeのソースコード 呼び出し元のContinuationとCoroutineContextを使って CoroutineScopeに紐づくコルーチン「ScopeCoroutine」が初期化‧起動される
Slide 71
Slide 71 text
ScopeCoroutineのソースコード
Slide 72
Slide 72 text
ScopeCoroutineのソースコード AbstractCoroutine (基底クラス) の初期化時にコルーチンの親⼦構造が形成される
Slide 73
Slide 73 text
コルーチンの親⼦構造の形成 親のCoroutineContextに含まれる 親コルーチンのJobが initParentJobに渡される 親コルーチンのJobに対して ⼦コルーチンをattachChildすることで Jobの親⼦構造が形成
Slide 74
Slide 74 text
コルーチンの親⼦構造の形成 (イメージ図) mainのContinuation CoroutineContext Job coroutineScopeのコルーチン CoroutineContext Job attachChild launchのコルーチン CoroutineContext Job launchのコルーチン CoroutineContext Job CoroutineContextが 初期化時に渡される 親のCoroutineContextに含まれるJobに対してattachChildすることで コルーチン (Job) の親⼦構造が形成される attachChild
Slide 75
Slide 75 text
ScopeCoroutineのソースコード (再掲)
Slide 76
Slide 76 text
ScopeCoroutineのソースコード (再掲) afterCompletionかafterResumeが呼ばれると 呼び出し元のContinuationのresumeWithが呼ばれ、coroutineScopeを抜ける (呼ばれる条件が分かれば、CoroutineScopeが⼦コルーチンの完了を待つ仕組みが分かる!)
Slide 77
Slide 77 text
Jobの役割 Ref) https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/ New Active Completing Completed ⼦Jobの完了待ち Cancelling キャンセル/失敗 Cancelled ● コルーチンの状態管理 ● コルーチンの親⼦構造の管理 (および親⼦構造を通じた状態の伝播) Jobの状態遷移図 ⼦Jobのキャンセル待ち
Slide 78
Slide 78 text
Coroutine Scopeが⼦コルーチンの完了を待つ仕組み Ref) https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-core/common/src/JobSupport.kt Coroutine Context Job (ACTIVE) mainのContinuation
Slide 79
Slide 79 text
Coroutine Scopeが⼦コルーチンの完了を待つ仕組み Ref) https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-core/common/src/JobSupport.kt Coroutine Context Job (ACTIVE) mainのContinuation Coroutine Context Job (ACTIVE) ScopeCoroutine ScopeCoroutineが起動し 「mainのJob」と「ScopeCoroutineのJob」の間に親⼦構造が形成される
Slide 80
Slide 80 text
Coroutine Scopeが⼦コルーチンの完了を待つ仕組み Ref) https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-core/common/src/JobSupport.kt Coroutine Context Job (ACTIVE) mainのContinuation Coroutine Context ScopeCoroutine Coroutine Context Job (ACTIVE) Job (ACTIVE) StandaloneCoroutine #1 1つ⽬のlaunchによってStandaloneCoroutineが起動し 「ScopeCoroutineのJob」と「StandaloneCoroutine #1のJob」の間に親⼦構造が形成される
Slide 81
Slide 81 text
Coroutine Scopeが⼦コルーチンの完了を待つ仕組み Ref) https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-core/common/src/JobSupport.kt Coroutine Context Job (ACTIVE) mainのContinuation Coroutine Context ScopeCoroutine Coroutine Context Job (ACTIVE) Job (ACTIVE) StandaloneCoroutine #1 2つ⽬のlaunchによってStandaloneCoroutineが起動し 「ScopeCoroutineのJob」と「StandaloneCoroutine #2のJob」の間に親⼦構造が形成される Coroutine Context Job (ACTIVE) StandaloneCoroutine #2
Slide 82
Slide 82 text
Coroutine Scopeが⼦コルーチンの完了を待つ仕組み Ref) https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-core/common/src/JobSupport.kt Coroutine Context Job (ACTIVE) mainのContinuation Coroutine Context ScopeCoroutine Coroutine Context Job (ACTIVE) Job (COMPLETING) StandaloneCoroutine #1 `coroutineScope`内の処理が完了し ScopeCoroutineのJobがCOMPLETING (⼦コルーチンの完了待ち) になる Coroutine Context Job (ACTIVE) StandaloneCoroutine #2
Slide 83
Slide 83 text
AbstractCoroutine.resumeWithのソースコード coroutineScope内の処理の完了時に、ScopeCoroutineの`resumeWith`が呼ばれる 未完了の⼦コルーチンが残っている場合、returnして待機 ⼦コルーチンが全て完了している場合、`afterResume`を呼んでcoroutineScopeを抜ける https://github.com/Kotlin/kotlinx.coroutines/blob/8c27d51025d56a7b8de8eec2fb234b0094ce84f1/kotlinx-coroutines-core/common/src/AbstractCoroutine.kt#L98
Slide 84
Slide 84 text
Coroutine Scopeが⼦コルーチンの完了を待つ仕組み Ref) https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-core/common/src/JobSupport.kt Coroutine Context Job (ACTIVE) mainのContinuation Coroutine Context ScopeCoroutine Coroutine Context Job (ACTIVE) Job (COMPLETING) StandaloneCoroutine #1 `makeCompletingOnce`メソッド内で、いずれかの未完了の⼦コルーチンに対して 完了時に呼ばれるコールバック関数 (CompletionHandler) が登録される Coroutine Context Job (ACTIVE) StandaloneCoroutine #2 Completion Handler
Slide 85
Slide 85 text
Coroutine Scopeが⼦コルーチンの完了を待つ仕組み Ref) https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-core/common/src/JobSupport.kt Coroutine Context Job (ACTIVE) mainのContinuation Coroutine Context ScopeCoroutine Coroutine Context Job (ACTIVE) Job (COMPLETING) StandaloneCoroutine #1 StandaloneCoroutine #2が完了し、JobがCOMPLETEDになる Coroutine Context Job (COMPLETED) StandaloneCoroutine #2 Completion Handler
Slide 86
Slide 86 text
Coroutine Scopeが⼦コルーチンの完了を待つ仕組み Ref) https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-core/common/src/JobSupport.kt Coroutine Context Job (ACTIVE) mainのContinuation Coroutine Context ScopeCoroutine Coroutine Context Job (ACTIVE) Job (COMPLETING) StandaloneCoroutine #1 CompletionHandlerを介して、ScopeCoroutineのJobの`continueCompleting`が呼ばれる Coroutine Context Job (COMPLETED) StandaloneCoroutine #2 Completion Handler continueCompleting
Slide 87
Slide 87 text
continueCompletingのソースコード ※ 説明⽤に⼀部簡略化 https://github.com/Kotlin/kotlinx.coroutines/blob/8c27d51025d56a7b8de8eec2fb234b0094ce84f1/kotlinx-coroutines-core/common/src/JobSupport.kt#L965 未完了の⼦コルーチンが残っている場合 いずれかの未完了の⼦コルーチンにCompletionHandlerを登録してreturn 全ての⼦コルーチンが完了済みの場合 `afterCompletion`を呼んでcoroutineScopeを抜ける
Slide 88
Slide 88 text
Coroutine Scopeが⼦コルーチンの完了を待つ仕組み Ref) https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-core/common/src/JobSupport.kt Coroutine Context Job (ACTIVE) mainのContinuation Coroutine Context ScopeCoroutine Coroutine Context Job (ACTIVE) Job (COMPLETING) StandaloneCoroutine #1 Coroutine Context Job (COMPLETED) StandaloneCoroutine #2 Completion Handler `continueCompleting`メソッド内で、いずれかの未完了の⼦コルーチンに対して 完了時に呼ばれるコールバック関数 (CompletionHandler) が登録される
Slide 89
Slide 89 text
Coroutine Scopeが⼦コルーチンの完了を待つ仕組み Ref) https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-core/common/src/JobSupport.kt Coroutine Context Job (ACTIVE) mainのContinuation Coroutine Context ScopeCoroutine Coroutine Context Job (COMPLETED) Job (COMPLETING) StandaloneCoroutine #1 Coroutine Context Job (COMPLETED) StandaloneCoroutine #2 Completion Handler StandaloneCoroutine #1が完了し、JobがCOMPLETEDになる
Slide 90
Slide 90 text
Coroutine Scopeが⼦コルーチンの完了を待つ仕組み Ref) https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-core/common/src/JobSupport.kt Coroutine Context Job (ACTIVE) mainのContinuation Coroutine Context ScopeCoroutine Coroutine Context Job (COMPLETED) Job (COMPLETING) StandaloneCoroutine #1 Coroutine Context Job (COMPLETED) StandaloneCoroutine #2 Completion Handler CompletionHandlerを介して、ScopeCoroutineのJobの`continueCompleting`が呼ばれる continueCompleting
Slide 91
Slide 91 text
Coroutine Scopeが⼦コルーチンの完了を待つ仕組み Ref) https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-core/common/src/JobSupport.kt Coroutine Context Job (ACTIVE) mainのContinuation Coroutine Context ScopeCoroutine Coroutine Context Job (COMPLETED) Job (COMPLETED) StandaloneCoroutine #1 Coroutine Context Job (COMPLETED) StandaloneCoroutine #2 ⼦コルーチンが全て完了しているので、`afterCompleting`が呼ばれ、coroutineScopeを抜ける
Slide 92
Slide 92 text
Coroutine Scopeが⼦コルーチンの完了を待つ流れ `coroutineScope`内の処理実⾏が完了 未完了の⼦コルーチンが存在するか? afterResumeが呼ばれる いずれかの未完了の⼦コルーチンに CompletionHandlerを登録 CompletionHandlerが登録された ⼦コルーチンが完了 呼び出し元のresumeWithが呼ばれ、`coroutineScope`から抜ける 未完了の⼦コルーチンが存在するか? afterCompletionが呼ばれる NO YES NO YES
Slide 93
Slide 93 text
本発表のまとめ 1. Continuationの内部実装 → suspend関数は、どうやって⼀時停⽌‧再開しているのか? 2. Coroutine BuilderとDispatcherの内部実装 → コルーチンは、どのように⾮同期処理‧並⾏処理を実⾏するのか? 3. Coroutine ScopeとJobの内部実装 → Structured Concurrencyとは何か。そして、どのように実現されているのか?