iOS では、次のようなプロダクション クラッシュがいくつかあります。
Crashed: com.superdupertango.Server.CallbackRunQueue
0 SDTServer 0x102b78ed0 specialized Dictionary.subscript.getter + 4378562256 (<compiler-generated>:4378562256)
1 SDTServer 0x102a22fec closure #1 in Server.processError(_:) + 4377161708 (<compiler-generated>:4377161708)
2 SDTServer 0x10257ce90 thunk for @escaping @callee_guaranteed () -> () + 4372287120 (<compiler-generated>:4372287120)
これは問題のあるコードです (この領域のすべてのコードは Swift5 です):
private let callbackRunQueue = DispatchQueue(label: "com.superdupertango.Server.CallbackRunQueue", qos: DispatchQoS.userInitiated, target: nil)
var bodyCompletionBlocks: [String: ((Data?, Error?, String) -> Void)] = [:]
...
private init() {
NotificationCenter.default.addObserver(self, selector: #selector(self.processError(_:)), name: Notification.Name.SDTServerWriteFailed, object: nil)
}
...
@objc private func processError(_ notification: Notification) {
guard let userInfo = notification.userInfo, let requestId = userInfo["requestId"] as? String else {
return
}
self.callbackRunQueue.async {
DLog("Server.processError (\(requestId)): Got Error for request. Shutting down request.")
guard let bodyCompletionBlock = self.bodyCompletionBlocks[requestId] else {
DLog("Server.processError (\(requestId)): Failed getting body completion block. Returning")
return
}
bodyCompletionBlock(Data(), nil, requestId)
self.bodyCompletionBlocks.removeValue(forKey: requestId)
self.initialCompletionBlocks.removeValue(forKey: requestId)
}
}
callbackRunQueue
はシリアル キューであることに注意してください。
内部で取得される唯一の辞書値callbackRunQueue
は次のself.bodyCompletionBlocks
とおりです。
guard let bodyCompletionBlock = self.bodyCompletionBlocks[requestId] else {
このエラーに関するいくつかの参照を見つけましたが、それらはすべて、これはマルチスレッド アクセスの問題である可能性があると言っています。
ただし、私のコードでは、 にアクセスできる場所は 3 つしかなくself.bodyCompletionBlocks
、それらはすべてcallbackRunQueue.async
またはcallbackRunQueue.sync
ブロック内にあります。また、このコードの一部は GCDWebServer 内のスレッド内で実行されていることに注意してください。ただし、前述したように、コードが常にcallbackRunQueue
キューで実行されるようにしているため、これが GCDWebServer に関連しているとは思いません。
スレッドに依存するコードをシリアル キューの async ブロックまたは sync ブロック内に入れると、このようなマルチスレッド アクセスの問題を防ぐことができると思いました。
何か案は?
ありがとう!