浏览代码

Avoid race condition when scheduling a timeout (#847)

Motivation:

When creating an RPC a race condition was possible where the scheduled
task for a timeout would always be read from the event loop but not set
from the event loop.

Modifications:

Set the scheduled task on the event loop.

Result:

More thread safety.
George Barnett 5 年之前
父节点
当前提交
f3d77f4631
共有 1 个文件被更改,包括 24 次插入6 次删除
  1. 24 6
      Sources/GRPC/ClientCalls/ClientCallTransport.swift

+ 24 - 6
Sources/GRPC/ClientCalls/ClientCallTransport.swift

@@ -142,12 +142,7 @@ internal class ChannelTransport<Request: GRPCPayload, Response: GRPCPayload> {
     }
 
     // Schedule the timeout.
-    let deadline = timeLimit.makeDeadline()
-    if deadline != .distantFuture {
-      self.scheduledTimeout = eventLoop.scheduleTask(deadline: deadline) {
-        self.timedOut(after: timeLimit)
-      }
-    }
+    self.setUpTimeLimit(timeLimit)
 
     // Now attempt to make the channel.
     channelProvider(self, channelPromise)
@@ -590,6 +585,29 @@ extension ChannelTransport {
       self.stopwatch = nil
     }
   }
+
+  /// Sets a time limit for the RPC.
+  private func setUpTimeLimit(_ timeLimit: TimeLimit) {
+    let deadline = timeLimit.makeDeadline()
+
+    guard deadline != .distantFuture else {
+      // This is too distant to worry about.
+      return
+    }
+
+    let timedOutTask = {
+      self.timedOut(after: timeLimit)
+    }
+
+    // 'scheduledTimeout' must only be accessed from the event loop.
+    if self.eventLoop.inEventLoop {
+      self.scheduledTimeout = self.eventLoop.scheduleTask(deadline: deadline, timedOutTask)
+    } else {
+      self.eventLoop.execute {
+        self.scheduledTimeout = self.eventLoop.scheduleTask(deadline: deadline, timedOutTask)
+      }
+    }
+  }
 }