Sendable適合のためにclosureをOSAllocatedUnfairLockで保護することを考える。大体用途的にhandlerということでoptionalにしたい。
final class Po: Sendable { let handler: OSAllocatedUnfairLock<(@Sendable (Po.Result) -> ())?> = .init(initialState: nil) func setHandler(_ handler: (@Sendable (Po.Result) -> ())?) { self.handler.withLock { $0 = handler } } func call(with value: String) { let handler = handler.withLock { return $0 } handler?(.init(value: value)) } struct Result { let value: String } }
これは簡単すぎるコードで再現確認もとってないけど、だいたいこういう状況でhandlerを呼んだらstack overflow(EXC_BAD_ACCESS code=2)した。なぜ?
call stackの状況をプリントしてみる。
for symbol in Thread.callStackSymbols { print(symbol) }
結果がこう。
1 Example1Event.debug.dylib 0x0000000103f80ec0 $s12Example1Core10VideoFrameCIeghg_ACytIeghnr_TR + 24 2 Example1Event.debug.dylib 0x0000000103f80e98 $s12Example1Core10VideoFrameCytIeghnr_ACIeghg_TR + 36 3 Example1Event.debug.dylib 0x0000000103f80ec0 $s12Example1Core10VideoFrameCIeghg_ACytIeghnr_TR + 24 4 Example1Event.debug.dylib 0x0000000103f80e98 $s12Example1Core10VideoFrameCytIeghnr_ACIeghg_TR + 36 5 Example1Event.debug.dylib 0x0000000103f80ec0 $s12Example1Core10VideoFrameCIeghg_ACytIeghnr_TR + 24 6 Example1Event.debug.dylib 0x0000000103f80e98 $s12Example1Core10VideoFrameCytIeghnr_ACIeghg_TR + 36 7 Example1Event.debug.dylib 0x0000000103f80ec0 $s12Example1Core10VideoFrameCIeghg_ACytIeghnr_TR + 24 8 Example1Event.debug.dylib 0x0000000103f80e98 $s12Example1Core10VideoFrameCytIeghnr_ACIeghg_TR + 36 9 Example1Event.debug.dylib 0x0000000103f80ec0 $s12Example1Core10VideoFrameCIeghg_ACytIeghnr_TR + 24 10 Example1Event.debug.dylib 0x0000000103f80e98 $s12Example1Core10VideoFrameCytIeghnr_ACIeghg_TR + 36 11 Example1Event.debug.dylib 0x0000000103f80ec0 $s12Example1Core10VideoFrameCIeghg_ACytIeghnr_TR + 24 12 Example1Event.debug.dylib 0x0000000103f80e98 $s12Example1Core10VideoFrameCytIeghnr_ACIeghg_TR + 36 13 Example1Event.debug.dylib 0x0000000103f80ec0 $s12Example1Core10VideoFrameCIeghg_ACytIeghnr_TR + 24 14 Example1Event.debug.dylib 0x0000000103f80e98 $s12Example1Core10VideoFrameCytIeghnr_ACIeghg_TR + 36 15 Example1Event.debug.dylib 0x0000000103f80ec0 $s12Example1Core10VideoFrameCIeghg_ACytIeghnr_TR + 24 16 Example1Event.debug.dylib 0x0000000103f80e98 $s12Example1Core10VideoFrameCytIeghnr_ACIeghg_TR + 36 17 Example1Event.debug.dylib 0x0000000103f80ec0 $s12Example1Core10VideoFrameCIeghg_ACytIeghnr_TR + 24 18 Example1Event.debug.dylib 0x0000000103f80e98 $s12Example1Core10VideoFrameCytIeghnr_ACIeghg_TR + 36 19 Example1Event.debug.dylib 0x0000000103f80ec0 $s12Example1Core10VideoFrameCIeghg_ACytIeghnr_TR + 24 20 Example1Event.debug.dylib 0x0000000103f80e98 $s12Example1Core10VideoFrameCytIeghnr_ACIeghg_TR + 36 21 Example1Event.debug.dylib 0x0000000103f80ec0 $s12Example1Core10VideoFrameCIeghg_ACytIeghnr_TR + 24 22 Example1Event.debug.dylib 0x0000000103f80e98 $s12Example1Core10VideoFrameCytIeghnr_ACIeghg_TR + 36 23 Example1Event.debug.dylib 0x0000000103f80ec0 $s12Example1Core10VideoFrameCIeghg_ACytIeghnr_TR + 24 24 Example1Event.debug.dylib 0x0000000103f80e98 $s12Example1Core10VideoFrameCytIeghnr_ACIeghg_TR + 36 25 Example1Event.debug.dylib 0x0000000103f80ec0 $s12Example1Core10VideoFrameCIeghg_ACytIeghnr_TR + 24 26 Example1Event.debug.dylib 0x0000000103f80e98 $s12Example1Core10VideoFrameCytIeghnr_ACIeghg_TR + 36 27 Example1Event.debug.dylib 0x0000000103f80ec0 $s12Example1Core10VideoFrameCIeghg_ACytIeghnr_TR + 24 28 Example1Event.debug.dylib 0x0000000103f80e98 $s12Example1Core10VideoFrameCytIeghnr_ACIeghg_TR + 36 29 Example1Event.debug.dylib 0x0000000103f80ec0 $s12Example1Core10VideoFrameCIeghg_ACytIeghnr_TR + 24 30 Example1Event.debug.dylib 0x0000000103f80e98 $s12Example1Core10VideoFrameCytIeghnr_ACIeghg_TR + 36 31 Example1Event.debug.dylib 0x0000000103f80ec0 $s12Example1Core10VideoFrameCIeghg_ACytIeghnr_TR + 24 32 Example1Event.debug.dylib 0x0000000103f80e98 $s12Example1Core10VideoFrameCytIeghnr_ACIeghg_TR + 36 33 Example1Event.debug.dylib 0x0000000103f80ec0 $s12Example1Core10VideoFrameCIeghg_ACytIeghnr_TR + 24 34 Example1Event.debug.dylib 0x0000000103f80e98 $s12Example1Core10VideoFrameCytIeghnr_ACIeghg_TR + 36 35 Example1Event.debug.dylib 0x0000000103f80ec0 $s12Example1Core10VideoFrameCIeghg_ACytIeghnr_TR + 24 36 Example1Event.debug.dylib 0x0000000103f80e98 $s12Example1Core10VideoFrameCytIeghnr_ACIeghg_TR + 36 37 Example1Event.debug.dylib 0x0000000103f80ec0 $s12Example1Core10VideoFrameCIeghg_ACytIeghnr_TR + 24 38 Example1Event.debug.dylib 0x0000000103f80e98 $s12Example1Core10VideoFrameCytIeghnr_ACIeghg_TR + 36 39 Example1Event.debug.dylib 0x0000000103f82878 $s12Example1Core13CameraCaptureC13captureOutput_03didF04fromySo09AVCaptureF0C_So17CMSampleBufferRefaSo0I10ConnectionCtF + 636 40 Example1Event.debug.dylib 0x0000000103f83018 $s12Example1Core13CameraCaptureC13captureOutput_03didF04fromySo09AVCaptureF0C_So17CMSampleBufferRefaSo0I10ConnectionCtFTo + 116 41 AVFCapture 0x00000001a9e96fac 31134C66-3B30-3B0E-B11A-00CEFB451A52 + 163756 42 AVFCapture 0x00000001a9ebad48 31134C66-3B30-3B0E-B11A-00CEFB451A52 + 310600 43 CMCapture 0x00000001ad70b3a0 E68AFBBE-7145-38E8-B796-F5399CE474DB + 844704 44 CMCapture 0x00000001ad70b010 E68AFBBE-7145-38E8-B796-F5399CE474DB + 843792 45 libdispatch.dylib 0x0000000102f6e7bc _dispatch_client_callout + 20 46 libdispatch.dylib 0x0000000102f718e0 _dispatch_continuation_pop + 676 47 libdispatch.dylib 0x0000000102f88cc8 _dispatch_source_latch_and_call + 480 48 libdispatch.dylib 0x0000000102f87718 _dispatch_source_invoke + 860 49 libdispatch.dylib 0x0000000102f764a4 _dispatch_lane_serial_drain + 376 50 libdispatch.dylib 0x0000000102f77408 _dispatch_lane_invoke + 408 51 libdispatch.dylib 0x0000000102f84404 _dispatch_root_queue_drain_deferred_wlh + 328 52 libdispatch.dylib 0x0000000102f83a38 _dispatch_workloop_worker_thread + 444 53 libsystem_pthread.dylib 0x00000001e9804934 _pthread_wqthread + 288 54 libsystem_pthread.dylib 0x00000001e98010cc start_wqthread + 8
終わってるcall stack。2つの再帰してる呼び出しをdemangleしてみる。
~ swift demangle '$s12Example1Core10VideoFrameCytIeghnr_ACIeghg_TR' $s12Example1Core10VideoFrameCytIeghnr_ACIeghg_TR ---> reabstraction thunk helper from @escaping @callee_guaranteed @Sendable (@in_guaranteed Example1Core.VideoFrame) -> (@out ()) to @escaping @callee_guaranteed @Sendable (@guaranteed Example1Core.VideoFrame) -> () ~ swift demangle '$s12Example1Core10VideoFrameCIeghg_ACytIeghnr_TR' $s12Example1Core10VideoFrameCIeghg_ACytIeghnr_TR ---> reabstraction thunk helper from @escaping @callee_guaranteed @Sendable (@guaranteed Example1Core.VideoFrame) -> () to @escaping @callee_guaranteed @Sendable (@in_guaranteed Example1Core.VideoFrame) -> (@out ())
取り出して差分強調
- reabstraction thunk helper from @escaping @callee_guaranteed @Sendable (@in_guaranteed Example1Core.VideoFrame) -> (@out ()) to @escaping @callee_guaranteed @Sendable (@guaranteed Example1Core.VideoFrame) -> ()
- reabstraction thunk helper from @escaping @callee_guaranteed @Sendable (@guaranteed Example1Core.VideoFrame) -> () to @escaping @callee_guaranteed @Sendable (@in_guaranteed Example1Core.VideoFrame) -> (@out ())
なんか知らない reabstraction thunk helper なるものが作られて呼ばれている上に、それが無限に相互に変換し合っている。なんでこんな事が起こっているか厳密には理解していないので言及は避けるが、OSAllocatedUnfairLockに型パラメータとして渡る型、setterで受け取る型、利用側で取り出す型それぞれについてシグネチャは同じだがコンパイラから見ると差分があるらしくこの変換が自動生成されるっぽい。
取り急ぎ対策としてはOSAllocatedUnfairLockにわたす際に一段型を挟んで型のブレを防ぐ。
struct HandlerBox<Handler: Sendable>: Sendable {
let handler: Handler
public init(handler: Handler) {
self.handler = handler
}
}
全く意味のなさそうな型だが
final class Po: Sendable {
let handler: OSAllocatedUnfairLock<HandlerBox<(@Sendable (Po.Result) -> ())>?> = .init(initialState: nil)
func setHandler(_ handler: (@Sendable (Po.Result) -> ())?) {
self.handler.withLock { $0 = handler.map { .init(handler: $0) }}
}
func call(with value: String) {
let box = handler.withLock { return $0 }
box?.handler(.init(value: value))
}
struct Result {
let value: String
}
}
にすると上記のstack traceの再帰呼び出しは解消された。ふわっとした記事だけどハマる人(自分)のために念の為。