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
#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 () {
@private
GCDWebServerMatchBlock _matchBlock;
@ -129,9 +144,10 @@ static void _SignalHandler(int signal) {
dispatch_queue_t _syncQueue;
dispatch_semaphore_t _sourceSemaphore;
NSMutableArray* _handlers;
NSInteger _activeConnections; // Accessed only with _syncQueue
BOOL _connected;
CFRunLoopTimerRef _connectedTimer;
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;
NSString* _serverName;
@ -175,10 +191,13 @@ static void _SignalHandler(int signal) {
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 {
[(ARC_BRIDGE GCDWebServer*)info _didDisconnect];
[server _didDisconnect];
}
server->_disconnecting = NO;
}
- (instancetype)init {
@ -187,8 +206,8 @@ static void _ConnectedTimerCallBack(CFRunLoopTimerRef timer, void* info) {
_sourceSemaphore = dispatch_semaphore_create(0);
_handlers = [[NSMutableArray alloc] init];
CFRunLoopTimerContext context = {0, (ARC_BRIDGE void*)self, NULL, NULL, NULL};
_connectedTimer = CFRunLoopTimerCreate(kCFAllocatorDefault, HUGE_VAL, HUGE_VAL, 0, 0, _ConnectedTimerCallBack, &context);
CFRunLoopAddTimer(CFRunLoopGetMain(), _connectedTimer, kCFRunLoopCommonModes);
_disconnectTimer = CFRunLoopTimerCreate(kCFAllocatorDefault, HUGE_VAL, HUGE_VAL, 0, 0, _DisconnectTimerCallBack, &context);
CFRunLoopAddTimer(CFRunLoopGetMain(), _disconnectTimer, kCFRunLoopCommonModes);
#if TARGET_OS_IPHONE
_backgroundTask = UIBackgroundTaskInvalid;
#endif
@ -201,8 +220,8 @@ static void _ConnectedTimerCallBack(CFRunLoopTimerRef timer, void* info) {
DCHECK(_activeConnections == 0);
DCHECK(_options == nil); // The server can never be dealloc'ed while running because of the retain-cycle with the dispatch source
CFRunLoopTimerInvalidate(_connectedTimer);
CFRelease(_connectedTimer);
CFRunLoopTimerInvalidate(_disconnectTimer);
CFRelease(_disconnectTimer);
ARC_RELEASE(_handlers);
ARC_DISPATCH_RELEASE(_sourceSemaphore);
ARC_DISPATCH_RELEASE(_syncQueue);
@ -252,8 +271,9 @@ static void _ConnectedTimerCallBack(CFRunLoopTimerRef timer, void* info) {
DCHECK(_activeConnections >= 0);
if (_activeConnections == 0) {
dispatch_async(dispatch_get_main_queue(), ^{
if (_disconnectDelay > 0.0) {
CFRunLoopTimerSetNextFireDate(_connectedTimer, HUGE_VAL);
if (_disconnecting) {
CFRunLoopTimerSetNextFireDate(_disconnectTimer, HUGE_VAL);
_disconnecting = NO;
}
if (_connected == NO) {
[self _didConnect];
@ -307,7 +327,8 @@ static void _ConnectedTimerCallBack(CFRunLoopTimerRef timer, void* info) {
if (_activeConnections == 0) {
dispatch_async(dispatch_get_main_queue(), ^{
if ((_disconnectDelay > 0.0) && (_source != NULL)) {
CFRunLoopTimerSetNextFireDate(_connectedTimer, CFAbsoluteTimeGetCurrent() + _disconnectDelay);
CFRunLoopTimerSetNextFireDate(_disconnectTimer, CFAbsoluteTimeGetCurrent() + _disconnectDelay);
_disconnecting = YES;
} else {
[self _didDisconnect];
}
@ -531,6 +552,14 @@ static inline NSString* _EncodeBase64(NSString* string) {
ARC_RELEASE(_authenticationDigestAccounts);
_authenticationDigestAccounts = nil;
dispatch_async(dispatch_get_main_queue(), ^{
if (_disconnecting) {
CFRunLoopTimerSetNextFireDate(_disconnectTimer, HUGE_VAL);
_disconnecting = NO;
[self _didDisconnect];
}
});
LOG_INFO(@"%@ stopped", [self class]);
if ([_delegate respondsToSelector:@selector(webServerDidStop:)]) {
dispatch_async(dispatch_get_main_queue(), ^{
@ -674,11 +703,7 @@ static inline NSString* _EncodeBase64(NSString* string) {
[self stop];
success = YES;
}
while (1) {
if (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, true) == kCFRunLoopRunTimedOut) { // Ensure pending scheduled callbacks have been executed
break;
}
}
_ExecuteMainThreadRunLoopSources();
signal(SIGINT, intHandler);
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
NSInteger result = -1;
if ([self startWithOptions:options error:NULL]) {
_ExecuteMainThreadRunLoopSources();
result = 0;
NSArray* files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:NULL];
@ -1073,15 +1099,12 @@ static void _LogResult(NSString* format, ...) {
++result;
}
}
_ExecuteMainThreadRunLoopSources();
}
[self stop];
while (1) {
if (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, true) == kCFRunLoopRunTimedOut) { // Ensure pending scheduled callbacks have been executed
break;
}
}
_ExecuteMainThreadRunLoopSources();
}
return result;
}