У меня есть несколько производственных сбоев на 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.
Я думал, что включение чувствительного к потокам кода в блоки asyn c или syn c последовательной очереди защитит от многопоточных проблем с доступом, подобных этой.
Есть идеи?
Спасибо!