Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Kotlin Fest 2025 - 内部実装から理解する Coroutines ― Cont...
Search
kaseken (Kent Kaseda)
October 30, 2025
1.9k
3
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
More Decks by kaseken (Kent Kaseda)
See All by kaseken (Kent Kaseda)
iOSDC Japan 2026 - Swiftで作って学ぼう!データベース自作入門
kaseken
2
110
Featured
See All Featured
svc-hook: hooking system calls on ARM64 by binary rewriting
retrage
2
570
How to Align SEO within the Product Triangle To Get Buy-In & Support - #RIMC
aleyda
2
1.8k
The innovator’s Mindset - Leading Through an Era of Exponential Change - McGill University 2025
jdejongh
PRO
1
330
Tell your own story through comics
letsgokoyo
1
1.1k
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
PRO
202
76k
Optimizing for Happiness
mojombo
378
71k
DBのスキルで生き残る技術 - AI時代におけるテーブル設計の勘所
soudai
PRO
68
57k
Exploring the relationship between traditional SERPs and Gen AI search
raygrieselhuber
PRO
2
4.3k
Digital Ethics as a Driver of Design Innovation
axbom
PRO
1
420
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
27k
My Coaching Mixtape
mlcsv
0
310
Git: the NoSQL Database
bkeepers
PRO
432
67k
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とは何か。そして、どのように実現されているのか?