Swift/Sendable
#Swift
環境
code:sh
$swift --version
swift-driver version: 1.75.2 Apple Swift version 5.8 (swiftlang-5.8.0.124.2 clang-1403.0.22.11.100)
Target: arm64-apple-macosx13.0
/icons/hr.icon
Sendable
値をコピーすることでconcurrency domain間で安全に渡すことができる型
iOS8.0から
Swift Concurrency文脈からではない
→ swift-evolution/proposals/0302 より、Swift 5.7でリリースされたのは2022年9月。iOS 8.0が主流だった時期と大きくズレていることから、バックポート対応をしたのだろう
https://github.com/apple/swift/releases/tag/swift-5.7-RELEASE
どういう状況でSendableを使うのか?
ある値をあるconccurency domainから別のconcyrrency domainに安全に渡したい時
例えばActorのメソッドを呼び出す時にSendableに適合している型の値を引数として渡すことができる
/icons/hr.icon
メモ
Concurrency | Documentation
リファクタの例がよく分からなかった
code:swift
let firstPhoto = await listPhotos(inGallery: "Summer Vacation")0
add(firstPhoto, toGallery: "Road Trip")
// At this point, firstPhoto is temporarily in both galleries.
remove(firstPhoto, fromGallery: "Summer Vacation")
// ↓
func move(_ photoName: String, from source: String, to destination: String) {
add(photoName, toGallery: destination)
remove(photoName, fromGallery: source)
}
// ...
let firstPhoto = await listPhotos(inGallery: "Summer Vacation")0
move(firstPhoto, from: "Summer Vacation", to: "Road Trip")
→ リファクタ前でも動作するが、もしaddとremoveの間に今後awaitなメソッド/関数の呼び出しを追加した場合に、不整合が起こるからまとめよう、という話と理解
code:swift
add(firstPhoto, toGallery: "Road Trip")
// await hoge() が入ると、hoge()の処理時間分だけremove()の処理開始時間が遅れる
remove(firstPhoto, fromGallery: "Summer Vacation")
→ 動作する: コンパイルは通るが、ランタイムで不整合が起こる可能性がある
Calling Asynchronous Functions in Parallel
code:swift
# 上から順繰りに実行される
let hoge = await fuga()
let hoge2 = await fuga()
# リソース次第ではあるが同時に実行される
async let hoge = fuga()
async let hoge2 = fuga()
let hoges = await hoge, hoge2
awaitを付けないのはブロックせずにメソッド/関数の結果を待つことがないから
awaitまで一気に実行される
awaitがあるのでブロックされる可能性があることを明に示している
Tasks and Task Groups
Task、非同期的なコンテキストでない箇所で非同期処理を使うために書く、ぐらいの知識しかない
A task is a unit of work that can be run asynchronously as part of your program.
すべての非同期コードは何らかのTaskの一部として実行される
async-let構文では子Taskを作成する
Task Groupsは子Taskを持つといった関係はstructured concurrencyと呼ぶ
↔ unstructured concurrency
親Taskを持たない
現在のActorで実行するunstructured concurrencyを作成: Task.init
現在のActorの一部ではないunstructured concurrencyを作成: Task.detached
Task Cancellation
Taskのキャンセル時のハンドリングは適当な例で書いたほうが理解しやすそうだな
→ Swift/Taskを触る
Actors
Swift/Actor
Actorは一度に一つのTaskだけがmutableなstateにアクセスすることができる
Actor内のプロパティアクセスにはawaitは不要 vs Actor外から一つのTask経由でアクセスする際に、矛盾が生じるのでは?
→ awaitが書かれる側、つまりActor外からのアクセス時のほうが中断される
awaitがある場合はブロックされる可能性がある、というルールの適用
Sendable Types
mutableなプロパティを含むクラスで、そのプロパティへのアクセスをシリアライズ(= 逐次化)しない場合はデータの不整合が起こり得る。そのためにSendableを使う。
Sendableに適合している型を持つStructは暗黙的にSendableに適用される、みたいものは他のProtocolに準拠したケースも同様なのでスッと頭に入った
@usableFromInline
初めて見た
コンパイル時の話っぽい?
→ WIP/Swift/@inlinable, @usableFromInline
参考
Sendable | Apple Developer Documentation
Sendable and @Sendable closures explained with code examples
スレッドセーフとは - 意味をわかりやすく - IT用語辞典 e-Words
Concurrency | Documentation
swift-evolution/proposals/0302-concurrent-value-and-concurrent-closures.md at main · apple/swift-evolution · GitHub
シリアライズ - Wikipedia