1

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 ブロック内に入れると、このようなマルチスレッド アクセスの問題を防ぐことができると思いました。

何か案は?

ありがとう!

4

0 に答える 0