Browse Source

Fixed rare race-condition with disconnection timer

Pierre-Olivier Latour 11 years ago
parent
commit
54d5abd3a8
1 changed files with 20 additions and 23 deletions
  1. 20 23
      GCDWebServer/Core/GCDWebServer.m

+ 20 - 23
GCDWebServer/Core/GCDWebServer.m

@@ -147,7 +147,6 @@ static void _ExecuteMainThreadRunLoopSources() {
   NSMutableArray* _handlers;
   NSInteger _activeConnections;  // Accessed through _syncQueue only
   BOOL _connected;  // Accessed on main thread only
-  BOOL _disconnecting;  // Accessed on main thread only
   CFRunLoopTimerRef _disconnectTimer;  // Accessed on main thread only
   
   NSDictionary* _options;
@@ -193,23 +192,11 @@ static void _ExecuteMainThreadRunLoopSources() {
   GCDWebServerInitializeFunctions();
 }
 
-static void _DisconnectTimerCallBack(CFRunLoopTimerRef timer, void* info) {
-  DCHECK([NSThread isMainThread]);
-  GCDWebServer* server = (ARC_BRIDGE GCDWebServer*)info;
-  @autoreleasepool {
-    [server _didDisconnect];
-  }
-  server->_disconnecting = NO;
-}
-
 - (instancetype)init {
   if ((self = [super init])) {
     _syncQueue = dispatch_queue_create([NSStringFromClass([self class]) UTF8String], DISPATCH_QUEUE_SERIAL);
     _sourceSemaphore = dispatch_semaphore_create(0);
     _handlers = [[NSMutableArray alloc] init];
-    CFRunLoopTimerContext context = {0, (ARC_BRIDGE void*)self, NULL, NULL, NULL};
-    _disconnectTimer = CFRunLoopTimerCreate(kCFAllocatorDefault, HUGE_VAL, HUGE_VAL, 0, 0, _DisconnectTimerCallBack, &context);
-    CFRunLoopAddTimer(CFRunLoopGetMain(), _disconnectTimer, kCFRunLoopCommonModes);
 #if TARGET_OS_IPHONE
     _backgroundTask = UIBackgroundTaskInvalid;
 #endif
@@ -221,9 +208,8 @@ static void _DisconnectTimerCallBack(CFRunLoopTimerRef timer, void* info) {
   DCHECK(_connected == NO);
   DCHECK(_activeConnections == 0);
   DCHECK(_options == nil);  // The server can never be dealloc'ed while running because of the retain-cycle with the dispatch source
+  DCHECK(_disconnectTimer == NULL);  // The server can never be dealloc'ed while the disconnect timer is pending because of the retain-cycle
   
-  CFRunLoopTimerInvalidate(_disconnectTimer);
-  CFRelease(_disconnectTimer);
   ARC_RELEASE(_handlers);
   ARC_DISPATCH_RELEASE(_sourceSemaphore);
   ARC_DISPATCH_RELEASE(_syncQueue);
@@ -273,9 +259,10 @@ static void _DisconnectTimerCallBack(CFRunLoopTimerRef timer, void* info) {
     DCHECK(_activeConnections >= 0);
     if (_activeConnections == 0) {
       dispatch_async(dispatch_get_main_queue(), ^{
-        if (_disconnecting) {
-          CFRunLoopTimerSetNextFireDate(_disconnectTimer, HUGE_VAL);
-          _disconnecting = NO;
+        if (_disconnectTimer) {
+          CFRunLoopTimerInvalidate(_disconnectTimer);
+          CFRelease(_disconnectTimer);
+          _disconnectTimer = NULL;
         }
         if (_connected == NO) {
           [self _didConnect];
@@ -329,8 +316,17 @@ static void _DisconnectTimerCallBack(CFRunLoopTimerRef timer, void* info) {
     if (_activeConnections == 0) {
       dispatch_async(dispatch_get_main_queue(), ^{
         if ((_disconnectDelay > 0.0) && (_source != NULL)) {
-          CFRunLoopTimerSetNextFireDate(_disconnectTimer, CFAbsoluteTimeGetCurrent() + _disconnectDelay);
-          _disconnecting = YES;
+          if (_disconnectTimer) {
+            CFRunLoopTimerInvalidate(_disconnectTimer);
+            CFRelease(_disconnectTimer);
+          }
+          _disconnectTimer = CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + _disconnectDelay, 0.0, 0, 0, ^(CFRunLoopTimerRef timer) {
+            DCHECK([NSThread isMainThread]);
+            [self _didDisconnect];
+            CFRelease(_disconnectTimer);
+            _disconnectTimer = NULL;
+          });
+          CFRunLoopAddTimer(CFRunLoopGetMain(), _disconnectTimer, kCFRunLoopCommonModes);
         } else {
           [self _didDisconnect];
         }
@@ -592,9 +588,10 @@ static inline NSString* _EncodeBase64(NSString* string) {
   _authenticationDigestAccounts = nil;
   
   dispatch_async(dispatch_get_main_queue(), ^{
-    if (_disconnecting) {
-      CFRunLoopTimerSetNextFireDate(_disconnectTimer, HUGE_VAL);
-      _disconnecting = NO;
+    if (_disconnectTimer) {
+      CFRunLoopTimerInvalidate(_disconnectTimer);
+      CFRelease(_disconnectTimer);
+      _disconnectTimer = NULL;
       [self _didDisconnect];
     }
   });