Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Kotlin Fest 2025 - 内部実装から理解する Coroutines ― Cont...
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
kaseken (Kent Kaseda)
October 30, 2025
1.6k
2
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Kotlin Fest 2025 - 内部実装から理解する Coroutines ― Continuation・Structured Concurrency・Dispatcher
kaseken (Kent Kaseda)
October 30, 2025
Featured
See All Featured
エンジニアに許された特別な時間の終わり
watany
107
250k
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
31
2.8k
Facilitating Awesome Meetings
lara
57
7k
ReactJS: Keep Simple. Everything can be a component!
pedronauck
666
130k
Groundhog Day: Seeking Process in Gaming for Health
codingconduct
0
200
Exploring the relationship between traditional SERPs and Gen AI search
raygrieselhuber
PRO
2
4k
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
37
6.5k
The MySQL Ecosystem @ GitHub 2015
samlambert
251
13k
What's in a price? How to price your products and services
michaelherold
247
13k
Why Your Marketing Sucks and What You Can Do About It - Sophie Logan
marketingsoph
0
170
How GitHub (no longer) Works
holman
316
150k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
27k
Transcript
内部実装から理解するCoroutines Continuation‧Structured Concurrency‧Dispatcher Kotlin Fest 2025 @kentkaseda kaseken kaseken
はじめに スライド: https://speakerdeck.com/kaseken/kotlin-fest-2025-nei-bu-shi-zhuang-karali-jie-suru-coroutines-c ontinuationstructured-concurrencydispatcher 原稿: https://zenn.dev/kaseken/articles/681c5aea0639c7
本発表のゴール 1. Kotlin Coroutinesの中⼼的なコンセプトを “仕組みから” 理解していただく 2. Kotlin Coroutinesのソースコードを気軽に読めるようになっていただく
Kotlin Coroutinesの中⼼的なコンセプト 1. Continuation suspend関数は、どのように⼀時停⽌‧再開しているのか? 2. Dispatcher コルーチンは、どのように⾮同期処理‧並⾏処理を実⾏するのか? 3. Structured
Concurrency Structured Concurrencyとは何か。そして、どのように実現されているのか?
1. Continuation suspend関数は、どのように⼀時停⽌‧再開しているのか? 2. Dispatcher コルーチンは、どのように⾮同期処理‧並⾏処理を実⾏するのか? 3. Structured Concurrency Structured
Concurrencyとは何か。そして、どのように実現されているのか? Kotlin Coroutinesの中⼼的なコンセプト
suspend関数の仕様 「中断」と「再開」がどのように実現されているのかを内部実装から追う delayが呼ばれた後「中断」する delayが完了した後「再開」する https://pl.kotl.in/awpNrOZhv
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
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
suspend関数のバイトコードを表⽰ IntelliJ IDEAの「Tools > Kotlin > Show Kotlin Bytecode」でBytecodeを表⽰
「Decompile」を実⾏すると Javaコードが復元される suspend関数のバイトコードをデコンパイル
suspend関数のバイトコードをデコンパイル ① 関数定義 ② Continuationの初期化 ③ suspend関数の実⾏
セクション① 関数定義 suspend関数の引数に「Continuation」が追加されている suspend関数は、HelloWorldKtクラスのstatic関数として定義される コンパイラによるバイトコードへの変換 デコンパイルによるJavaコードの復元
Continuationとは何か? • 各suspend関数は「Continuation」を持つ • Continuationの「resumeWith」を呼ぶと、対応するsuspend関数が再開する https://github.com/JetBrains/kotlin/blob/0075ea767c181d7a3b3b5c123a0d15fcc278e447/libraries/stdlib/src/kotlin/coroutines/Continuation.kt#L16 Continuationとは「中断されたsuspend関数を再開するためのハンドラ」
Continuationの役割をサンプルコードで解説 main関数 mainのContinuation 覚えてほしいポイント • 各suspend関数はContinuationを持つ • ContinuationのresumeWithを呼ぶと 対応するsuspend関数が再開する
main関数 helloWorld関数 mainのContinuation mainのContinuationが引数として渡される helloWorldの Continuation 覚えてほしいポイント • 各suspend関数はContinuationを持つ •
ContinuationのresumeWithを呼ぶと 対応するsuspend関数が再開する Continuationの役割をサンプルコードで解説
main関数 helloWorld関数 mainのContinuation helloWorldのContinuationが引数として渡される delay関数 helloWorldの Continuation 覚えてほしいポイント • 各suspend関数はContinuationを持つ
• ContinuationのresumeWithを呼ぶと 対応するsuspend関数が再開する Continuationの役割をサンプルコードで解説
main関数 helloWorld関数 mainのContinuation delay関数 helloWorldの Continuation helloWorldのContinuationのresumeWithを呼ぶと helloWorld関数が再開する 覚えてほしいポイント •
各suspend関数はContinuationを持つ • ContinuationのresumeWithを呼ぶと 対応するsuspend関数が再開する Continuationの役割をサンプルコードで解説
main関数 helloWorld関数 mainのContinuation helloWorldの Continuation mainのContinuationのresumeWithを呼ぶと main関数が再開する 覚えてほしいポイント • 各suspend関数はContinuationを持つ
• ContinuationのresumeWithを呼ぶと 対応するsuspend関数が再開する Continuationの役割をサンプルコードで解説
関数の引数に追加されたContinuationとは? 引数の「Continuation $completion」とは 呼び出し元の suspend関数に対応する Continuationである! …と言い切れれば単純だったが、実際にはこれは半分正解・半分不正解
セクション② suspend関数に紐づくContinuationの初期化
suspend関数の初回呼び出し時 呼び出し元のContinuationが渡される suspend関数に紐づくContinuationが初期化される
Continuationの初期化 invokeSuspendは再開時に呼ばれる すなわち、再開時にもHelloWorldKt.helloWorldが呼ばれる 再開時には、この関数のContinuationが渡される 「label」は、この関数がどこで再開すべきかを保持
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関数が再開する
suspend関数の再開時 この関数のContinuationが渡される (初回実行時は “呼び出し元” のContinuationが渡される) Continuationの持つ「label」が取得される
セクション③ suspend関数の実⾏
セクション③ suspend関数の実⾏:初回実⾏時 元の関数 ここまで実⾏ labelが1に変更
セクション③ suspend関数の実⾏:再開時 元の関数 ここから実⾏
まとめ:Continuationによるsuspend関数の中断‧再開の仕組み helloWorld関数 mainの Continuation main関数
まとめ:Continuationによるsuspend関数の中断‧再開の仕組み helloWorld関数 mainの Continuation main関数 helloWorldの Continuationを初期化
まとめ:Continuationによるsuspend関数の中断‧再開の仕組み helloWorld関数 mainの Continuation main関数 helloWorldの Continuationを初期化 label: 0の範囲のコードを実⾏ labelを1に変更
delay関数 helloWorldの Continuation 中断
まとめ:Continuationによるsuspend関数の中断‧再開の仕組み helloWorld関数 mainの Continuation main関数 helloWorldの Continuationを初期化 label: 0の範囲のコードを実⾏ labelを1に変更
delay関数 helloWorldの Continuation 中断 🕰 1秒経過 helloWorldのContinuationの resumeWithが呼ばれる 再開
まとめ:Continuationによるsuspend関数の中断‧再開の仕組み helloWorld関数 mainの Continuation main関数 helloWorldの Continuationを初期化 label: 0の範囲のコードを実⾏ labelを1に変更
delay関数 helloWorldの Continuation 中断 🕰 1秒経過 再開 label: 1の範囲のコードを実⾏ helloWorldのContinuationの resumeWithが呼ばれる mainのContinuationの resumeWithが呼ばれる
Kotlin Coroutinesの中⼼的なコンセプト 1. Continuation suspend関数は、どうやって⼀時停⽌‧再開しているのか? 2. Dispatcher コルーチンは、どのように⾮同期処理‧並⾏処理を実⾏するのか? 3. Structured
Concurrency Structured Concurrencyとは何か。そして、どのように実現されているのか?
Dispatcherの表⾯的な仕様 (Dispatchers.Default) • Coroutine Builder (launch) に引数を指定しない場合、Dispatchers.Default (CPU-boundなタスク⽤) が利⽤され、バックグラウンドで⾮同期実⾏される ◦
ID: 12, 13のバックグラウンドスレッドが使⽤されている https://pl.kotl.in/67I3RBx0v
Dispatcherの表⾯的な仕様 (Dispatchers.IO) https://pl.kotl.in/67I3RBx0v • Coroutine Builder (launch) の引数に指定することで、Dispatchers.IO (IO-boundなタスク⽤) を利⽤できる
◦ ID: 12, 13, 14, 15, 17のスレッドが使⽤されている
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
launchのソースコードを読む https://github.com/Kotlin/kotlinx.coroutines/blob/8c27d51025d56a7b8de8eec2fb234b0094ce84f1/kotlinx-coroutines-core/common/src/Builders.common.kt#L44 Coroutine Builderの代表例として「launch」のソースコードを読む
launchのソースコードを読む https://github.com/Kotlin/kotlinx.coroutines/blob/8c27d51025d56a7b8de8eec2fb234b0094ce84f1/kotlinx-coroutines-core/common/src/Builders.common.kt#L44 コルーチンに紐づくCoroutineContext (= コルーチンに紐づくメタデータのコンテナ ) の初期化 Dispatcherも、CoroutineContextの一要素
launchのソースコードを読む https://github.com/Kotlin/kotlinx.coroutines/blob/8c27d51025d56a7b8de8eec2fb234b0094ce84f1/kotlinx-coroutines-core/common/src/Builders.common.kt#L44 コルーチン (StandaloneCoroutine) のインスタンスが初期化される
launchのソースコードを読む https://github.com/Kotlin/kotlinx.coroutines/blob/8c27d51025d56a7b8de8eec2fb234b0094ce84f1/kotlinx-coroutines-core/common/src/Builders.common.kt#L44 StandaloneCoroutine.startが呼ばれコルーチンが起動
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
startCoroutineCancellableのソースコードを読む https://github.com/Kotlin/kotlinx.coroutines/blob/8c27d51025d56a7b8de8eec2fb234b0094ce84f1/kotlinx-coroutines-core/common/src/intrinsics/Cancellable.kt#L23
startCoroutineCancellableのソースコードを読む コルーチンに紐づく Continuation createCoroutineIntercepted https://github.com/Kotlin/kotlinx.coroutines/blob/8c27d51025d56a7b8de8eec2fb234b0094ce84f1/kotlinx-coroutines-core/common/src/intrinsics/Cancellable.kt#L23 コルーチンに紐づくContinuation が初期化される
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される
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される
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
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される
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
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))
Dispatcherの内部実装を追う コルーチンに紐づく Continuation createCoroutineIntercepted intercepted() コルーチンに紐づく Continuation DispatchedContinuation resumeCancellableWith() Dispatcher
DispatchedContinuationがdispatchされる wrapされたContinuationのresumeWithを呼ぶ (= コルーチンの実行 ) launch
CoroutineDispatcherの種別 • Dispatchers.Default CPU-Boundな処理の実⾏に使⽤ • Dispatchers.IO IO-Boundな処理の実⾏に使⽤ (Dispatchers.Defaultと同じスレッドプールを利⽤) • Dispatchers.Main
メインスレッド (UIスレッド) で実⾏する CoroutineDispatcherの実装は、実⾏環境 (JVM/Native/Web) ごとに異なる Dispatchers.DefaultおよびDispatchers.IOのJVMにおける内部実装を調査
CoroutineDispatcher.dispatchが呼ばれた後の流れ Dispatchers.Default.dispatch (Ref) CoroutineScheduler.dispatch (Ref) Dispatchers.IO.dispatch (Ref) LimitedDispatchers.dispatch (Ref) SchedulerCoroutineDispatcher.dispatch
(Ref) CoroutineScheduler.dispatchの実装は複雑なため、模式図を用いて解説する (⚠ここからの説明は簡略化したもので、実態とは異なります )
Dispatchers.Defaultでコルーチンが実⾏される仕組み CoroutineScheduler TaskQueue (CPU-bound) TaskQueue (IO-bound) Dispatchers.Defaultに コルーチンをdispatch CoroutineSchedulerのCPU-boundなタスク⽤のキューに追加される Thread
Pool
CoroutineScheduler TaskQueue (CPU-bound) TaskQueue (IO-bound) Dispatchers.Defaultに コルーチンをdispatch タスクを処理するためのWorker (JavaのThreadの⼦クラス (Ref))
が作られる Worker (待機中) Dispatchers.Defaultでコルーチンが実⾏される仕組み Thread Pool
タスクの取得・実行 をループする CoroutineScheduler TaskQueue (CPU-bound) TaskQueue (IO-bound) Dispatchers.Defaultに コルーチンをdispatch Workerが起動され、タスクの取得‧コルーチン実⾏
(DispatchedTask.run) をループする Worker (タスク実行中) Dispatchers.Defaultでコルーチンが実⾏される仕組み Thread Pool
CoroutineScheduler TaskQueue (CPU-bound) TaskQueue (IO-bound) Dispatchers.Defaultに コルーチンをdispatch CPU-boundなタスクを処理中のWorkerの個数は、CPUのコア数以下に制限される (= 上限に達している場合には、dispatch時にWorkerの作成は⾏われない)
Worker (タスク実行中) Worker (タスク実行中) Dispatchers.Defaultでコルーチンが実⾏される仕組み Thread Pool
Dispatchers.IOでコルーチンが実⾏される仕組み CoroutineScheduler TaskQueue (CPU-bound) TaskQueue (IO-bound) Dispatchers.IOに コルーチンをdispatch Worker (タスク実行中)
Worker (タスク実行中) CoroutineSchedulerのIO-boundなタスク⽤のキューに追加される Thread Pool
CoroutineScheduler TaskQueue (CPU-bound) TaskQueue (IO-bound) Dispatchers.IOに コルーチンをdispatch Worker (タスク実行中) Worker
(タスク実行中) Worker (待機中) Dispatchers.IOでコルーチンが実⾏される仕組み Thread Pool タスクを処理するためのWorker (JavaのThreadの⼦クラス (Ref)) が作られる
CoroutineScheduler TaskQueue (CPU-bound) TaskQueue (IO-bound) Dispatchers.IOに コルーチンをdispatch Worker (タスク実行中) Worker
(タスク実行中) Worker (タスク実行中) タスクの取得・実行 をループする Dispatchers.IOでコルーチンが実⾏される仕組み Thread Pool Workerが起動され、タスクの取得‧コルーチン実⾏ (DispatchedTask.run) をループする (※Dispatchers.Defaultと同じスレッドプールを利⽤)
CoroutineScheduler TaskQueue (CPU-bound) TaskQueue (IO-bound) Dispatchers.IOに コルーチンをdispatch Worker (タスク実行中) Worker
(タスク実行中) Worker (タスク実行中) Worker (タスク実行中) IO-boundなタスクを処理中のWorkerの個数には上限がない (※ ただし、実質的には上限がある。前段でlimitedParallelismによる制限がある) Worker (タスク実行中) Dispatchers.IOでコルーチンが実⾏される仕組み Thread Pool
IO-boundなタスクを処理中のWorkerの個数には上限がない (※ ただし、実質的には上限がある。前段でlimitedParallelismによる制限がある) CoroutineScheduler TaskQueue (CPU-bound) TaskQueue (IO-bound) Dispatchers.IOに コルーチンをdispatch
Worker (タスク実行中) Worker (タスク実行中) Worker (タスク実行中) タスクの取得・実行 をループする Dispatchers.IOでコルーチンが実⾏される仕組み Thread Pool ⚠ ここまでの説明は簡略化したもので、実態とは異なります 実際には、Workerごとのローカルのタスクキューもあるなど、もっと複雑です 正確な実装を知りたい⽅は、CoroutineSchedulerのソースコード (Ref) を読んでください
limitedParallelismによる並列に実⾏されるコルーチン数の制限 limitedParallelism(2) limitedParallelism(10) https://pl.kotl.in/V1x5RYgiI
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)が適⽤
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
1. Continuation suspend関数は、どうやって⼀時停⽌‧再開しているのか? 2. Dispatcher コルーチンは、どのように⾮同期処理‧並⾏処理を実⾏するのか? 3. Structured Concurrency Structured
Concurrencyとは何か。そして、どのように実現されているのか? Kotlin Coroutinesの中⼼的なコンセプト
Structured Concurrencyとは Parent Coroutine Child Coroutine ⼦コルーチンが完了するまで親コルーチンが完了しないこと
Structured Concurrencyとは Parent Coroutine Child Coroutine Structured Concurrencyに反する場合、リソースリークのリスクがある Kotlin Coroutinesでは
(⼀般的な⽤法において) Structured Concurrencyが守られる ⚠リソースリークのリスク
Kotlin CoroutinesにおけるStructured Concurrency • CoroutineScopeは、⼦コルーチンが全て完了してからスコープを抜ける • 全てのコルーチンは、CoroutineScopeを起点に起動する必要がある ◦ Coroutine Builder
(launchやasync) はCoroutineScopeの拡張関数 Kotlin Coroutinesでは (⼀般的な⽤法において) Structured Concurrencyが守られる
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
coroutineScopeのソースコード 呼び出し元のContinuationとCoroutineContextを使って CoroutineScopeに紐づくコルーチン「ScopeCoroutine」が初期化‧起動される
ScopeCoroutineのソースコード
ScopeCoroutineのソースコード AbstractCoroutine (基底クラス) の初期化時にコルーチンの親⼦構造が形成される
コルーチンの親⼦構造の形成 親のCoroutineContextに含まれる 親コルーチンのJobが initParentJobに渡される 親コルーチンのJobに対して ⼦コルーチンをattachChildすることで Jobの親⼦構造が形成
コルーチンの親⼦構造の形成 (イメージ図) mainのContinuation CoroutineContext Job coroutineScopeのコルーチン CoroutineContext Job attachChild launchのコルーチン
CoroutineContext Job launchのコルーチン CoroutineContext Job CoroutineContextが 初期化時に渡される 親のCoroutineContextに含まれるJobに対してattachChildすることで コルーチン (Job) の親⼦構造が形成される attachChild
ScopeCoroutineのソースコード (再掲)
ScopeCoroutineのソースコード (再掲) afterCompletionかafterResumeが呼ばれると 呼び出し元のContinuationのresumeWithが呼ばれ、coroutineScopeを抜ける (呼ばれる条件が分かれば、CoroutineScopeが⼦コルーチンの完了を待つ仕組みが分かる!)
Jobの役割 Ref) https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/ New Active Completing Completed ⼦Jobの完了待ち Cancelling キャンセル/失敗
Cancelled • コルーチンの状態管理 • コルーチンの親⼦構造の管理 (および親⼦構造を通じた状態の伝播) Jobの状態遷移図 ⼦Jobのキャンセル待ち
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 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」の間に親⼦構造が形成される
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」の間に親⼦構造が形成される
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
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
AbstractCoroutine.resumeWithのソースコード coroutineScope内の処理の完了時に、ScopeCoroutineの`resumeWith`が呼ばれる 未完了の⼦コルーチンが残っている場合、returnして待機 ⼦コルーチンが全て完了している場合、`afterResume`を呼んでcoroutineScopeを抜ける https://github.com/Kotlin/kotlinx.coroutines/blob/8c27d51025d56a7b8de8eec2fb234b0094ce84f1/kotlinx-coroutines-core/common/src/AbstractCoroutine.kt#L98
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
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
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
continueCompletingのソースコード ※ 説明⽤に⼀部簡略化 https://github.com/Kotlin/kotlinx.coroutines/blob/8c27d51025d56a7b8de8eec2fb234b0094ce84f1/kotlinx-coroutines-core/common/src/JobSupport.kt#L965 未完了の⼦コルーチンが残っている場合 いずれかの未完了の⼦コルーチンにCompletionHandlerを登録してreturn 全ての⼦コルーチンが完了済みの場合 `afterCompletion`を呼んでcoroutineScopeを抜ける
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) が登録される
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になる
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
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を抜ける
Coroutine Scopeが⼦コルーチンの完了を待つ流れ `coroutineScope`内の処理実⾏が完了 未完了の⼦コルーチンが存在するか? afterResumeが呼ばれる いずれかの未完了の⼦コルーチンに CompletionHandlerを登録 CompletionHandlerが登録された ⼦コルーチンが完了 呼び出し元のresumeWithが呼ばれ、`coroutineScope`から抜ける
未完了の⼦コルーチンが存在するか? afterCompletionが呼ばれる NO YES NO YES
本発表のまとめ 1. Continuationの内部実装 → suspend関数は、どうやって⼀時停⽌‧再開しているのか? 2. Coroutine BuilderとDispatcherの内部実装 → コルーチンは、どのように⾮同期処理‧並⾏処理を実⾏するのか?
3. Coroutine ScopeとJobの内部実装 → Structured Concurrencyとは何か。そして、どのように実現されているのか?