Ensure connected state is updated immediately after calling -stop

This commit is contained in:
Pierre-Olivier Latour 2014-04-30 14:04:46 -07:00
parent c193860468
commit 40ea252ad6

View File

@ -95,6 +95,21 @@ static void _SignalHandler(int signal) {
#endif #endif
#if !TARGET_OS_IPHONE || defined(__GCDWEBSERVER_ENABLE_TESTING__)
// This utility function is used to ensure scheduled callbacks on the main thread are called when running the server synchronously
// https://developer.apple.com/library/mac/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html
// The main queue works with the applications run loop to interleave the execution of queued tasks with the execution of other event sources attached to the run loop
// TODO: Ensure all scheduled blocks on the main queue are also executed
static void _ExecuteMainThreadRunLoopSources() {
SInt32 result;
do {
result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.0, true);
} while (result == kCFRunLoopRunHandledSource);
}
#endif
@interface GCDWebServerHandler () { @interface GCDWebServerHandler () {
@private @private
GCDWebServerMatchBlock _matchBlock; GCDWebServerMatchBlock _matchBlock;
@ -129,9 +144,10 @@ static void _SignalHandler(int signal) {
dispatch_queue_t _syncQueue; dispatch_queue_t _syncQueue;
dispatch_semaphore_t _sourceSemaphore; dispatch_semaphore_t _sourceSemaphore;
NSMutableArray* _handlers; NSMutableArray* _handlers;
NSInteger _activeConnections; // Accessed only with _syncQueue NSInteger _activeConnections; // Accessed through _syncQueue only
BOOL _connected; BOOL _connected; // Accessed on main thread only
CFRunLoopTimerRef _connectedTimer; BOOL _disconnecting; // Accessed on main thread only
CFRunLoopTimerRef _disconnectTimer; // Accessed on main thread only
NSDictionary* _options; NSDictionary* _options;
NSString* _serverName; NSString* _serverName;
@ -175,10 +191,13 @@ static void _SignalHandler(int signal) {
GCDWebServerInitializeFunctions(); GCDWebServerInitializeFunctions();
} }
static void _ConnectedTimerCallBack(CFRunLoopTimerRef timer, void* info) { static void _DisconnectTimerCallBack(CFRunLoopTimerRef timer, void* info) {
DCHECK([NSThread isMainThread]);
GCDWebServer* server = (ARC_BRIDGE GCDWebServer*)info;
@autoreleasepool { @autoreleasepool {
[(ARC_BRIDGE GCDWebServer*)info _didDisconnect]; [server _didDisconnect];
} }
server->_disconnecting = NO;
} }
- (instancetype)init { - (instancetype)init {
@ -187,8 +206,8 @@ static void _ConnectedTimerCallBack(CFRunLoopTimerRef timer, void* info) {
_sourceSemaphore = dispatch_semaphore_create(0); _sourceSemaphore = dispatch_semaphore_create(0);
_handlers = [[NSMutableArray alloc] init]; _handlers = [[NSMutableArray alloc] init];
CFRunLoopTimerContext context = {0, (ARC_BRIDGE void*)self, NULL, NULL, NULL}; CFRunLoopTimerContext context = {0, (ARC_BRIDGE void*)self, NULL, NULL, NULL};
_connectedTimer = CFRunLoopTimerCreate(kCFAllocatorDefault, HUGE_VAL, HUGE_VAL, 0, 0, _ConnectedTimerCallBack, &context); _disconnectTimer = CFRunLoopTimerCreate(kCFAllocatorDefault, HUGE_VAL, HUGE_VAL, 0, 0, _DisconnectTimerCallBack, &context);
CFRunLoopAddTimer(CFRunLoopGetMain(), _connectedTimer, kCFRunLoopCommonModes); CFRunLoopAddTimer(CFRunLoopGetMain(), _disconnectTimer, kCFRunLoopCommonModes);
#if TARGET_OS_IPHONE #if TARGET_OS_IPHONE
_backgroundTask = UIBackgroundTaskInvalid; _backgroundTask = UIBackgroundTaskInvalid;
#endif #endif
@ -201,8 +220,8 @@ static void _ConnectedTimerCallBack(CFRunLoopTimerRef timer, void* info) {
DCHECK(_activeConnections == 0); 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(_options == nil); // The server can never be dealloc'ed while running because of the retain-cycle with the dispatch source
CFRunLoopTimerInvalidate(_connectedTimer); CFRunLoopTimerInvalidate(_disconnectTimer);
CFRelease(_connectedTimer); CFRelease(_disconnectTimer);
ARC_RELEASE(_handlers); ARC_RELEASE(_handlers);
ARC_DISPATCH_RELEASE(_sourceSemaphore); ARC_DISPATCH_RELEASE(_sourceSemaphore);
ARC_DISPATCH_RELEASE(_syncQueue); ARC_DISPATCH_RELEASE(_syncQueue);
@ -252,8 +271,9 @@ static void _ConnectedTimerCallBack(CFRunLoopTimerRef timer, void* info) {
DCHECK(_activeConnections >= 0); DCHECK(_activeConnections >= 0);
if (_activeConnections == 0) { if (_activeConnections == 0) {
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
if (_disconnectDelay > 0.0) { if (_disconnecting) {
CFRunLoopTimerSetNextFireDate(_connectedTimer, HUGE_VAL); CFRunLoopTimerSetNextFireDate(_disconnectTimer, HUGE_VAL);
_disconnecting = NO;
} }
if (_connected == NO) { if (_connected == NO) {
[self _didConnect]; [self _didConnect];
@ -307,7 +327,8 @@ static void _ConnectedTimerCallBack(CFRunLoopTimerRef timer, void* info) {
if (_activeConnections == 0) { if (_activeConnections == 0) {
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
if ((_disconnectDelay > 0.0) && (_source != NULL)) { if ((_disconnectDelay > 0.0) && (_source != NULL)) {
CFRunLoopTimerSetNextFireDate(_connectedTimer, CFAbsoluteTimeGetCurrent() + _disconnectDelay); CFRunLoopTimerSetNextFireDate(_disconnectTimer, CFAbsoluteTimeGetCurrent() + _disconnectDelay);
_disconnecting = YES;
} else { } else {
[self _didDisconnect]; [self _didDisconnect];
} }
@ -531,6 +552,14 @@ static inline NSString* _EncodeBase64(NSString* string) {
ARC_RELEASE(_authenticationDigestAccounts); ARC_RELEASE(_authenticationDigestAccounts);
_authenticationDigestAccounts = nil; _authenticationDigestAccounts = nil;
dispatch_async(dispatch_get_main_queue(), ^{
if (_disconnecting) {
CFRunLoopTimerSetNextFireDate(_disconnectTimer, HUGE_VAL);
_disconnecting = NO;
[self _didDisconnect];
}
});
LOG_INFO(@"%@ stopped", [self class]); LOG_INFO(@"%@ stopped", [self class]);
if ([_delegate respondsToSelector:@selector(webServerDidStop:)]) { if ([_delegate respondsToSelector:@selector(webServerDidStop:)]) {
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
@ -674,11 +703,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
[self stop]; [self stop];
success = YES; success = YES;
} }
while (1) { _ExecuteMainThreadRunLoopSources();
if (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, true) == kCFRunLoopRunTimedOut) { // Ensure pending scheduled callbacks have been executed
break;
}
}
signal(SIGINT, intHandler); signal(SIGINT, intHandler);
signal(SIGTERM, termHandler); signal(SIGTERM, termHandler);
} }
@ -971,6 +996,7 @@ static void _LogResult(NSString* format, ...) {
NSArray* ignoredHeaders = @[@"Date", @"Etag"]; // Dates are always different by definition and ETags depend on file system node IDs NSArray* ignoredHeaders = @[@"Date", @"Etag"]; // Dates are always different by definition and ETags depend on file system node IDs
NSInteger result = -1; NSInteger result = -1;
if ([self startWithOptions:options error:NULL]) { if ([self startWithOptions:options error:NULL]) {
_ExecuteMainThreadRunLoopSources();
result = 0; result = 0;
NSArray* files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:NULL]; NSArray* files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:NULL];
@ -1073,15 +1099,12 @@ static void _LogResult(NSString* format, ...) {
++result; ++result;
} }
} }
_ExecuteMainThreadRunLoopSources();
} }
[self stop]; [self stop];
while (1) { _ExecuteMainThreadRunLoopSources();
if (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, true) == kCFRunLoopRunTimedOut) { // Ensure pending scheduled callbacks have been executed
break;
}
}
} }
return result; return result;
} }