Browse Source

Fix a potential race condition where operation group completion handlers could get called twice, as well as an unsynchronized access to `CompletionQueue.operationGroups` that should be synchronized.

Daniel Alm 7 years ago
parent
commit
17b32f51bb
1 changed files with 13 additions and 11 deletions
  1. 13 11
      Sources/SwiftGRPC/Core/CompletionQueue.swift

+ 13 - 11
Sources/SwiftGRPC/Core/CompletionQueue.swift

@@ -102,8 +102,7 @@ class CompletionQueue {
                        completion: (() -> Void)?) {
     // run the completion queue on a new background thread
     DispatchQueue.global().async {
-      var running = true
-      while running {
+      spinloop: while true {
         let event = cgrpc_completion_queue_get_next_event(self.underlyingCompletionQueue, -1.0)
         switch event.type {
         case GRPC_OP_COMPLETE:
@@ -118,22 +117,25 @@ class CompletionQueue {
             self.operationGroupsMutex.synchronize {
               self.operationGroups[tag] = nil
             }
+          } else {
+            print("CompletionQueue.runToCompletion error: operation group with tag \(tag) not found")
           }
-          break
         case GRPC_QUEUE_SHUTDOWN:
-          running = false
-          for operationGroup in self.operationGroups.values {
+          self.operationGroupsMutex.lock()
+          let currentOperationGroups = self.operationGroups
+          self.operationGroups = [:]
+          self.operationGroupsMutex.unlock()
+          
+          for operationGroup in currentOperationGroups.values {
             operationGroup.success = false
             operationGroup.completion?(operationGroup)
           }
-          self.operationGroupsMutex.synchronize {
-            self.operationGroups = [:]
-          }
-          break
+          break spinloop
         case GRPC_QUEUE_TIMEOUT:
-          break
+          continue spinloop
         default:
-          break
+          print("CompletionQueue.runToCompletion error: unknown event type \(event.type)")
+          break spinloop
         }
       }
       if let completion = completion {