From cf8a0a8624021f497d880d186728860cb930fe6a Mon Sep 17 00:00:00 2001 From: thomasvl Date: Sun, 9 Dec 2012 01:12:24 +0000 Subject: CFRunLoopStop does NOT act like "join" and block until the thread is stopped. Use a lock to wait before returning. --- Foundation/GTMNSThread+Blocks.h | 1 + Foundation/GTMNSThread+Blocks.m | 31 +++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) (limited to 'Foundation') diff --git a/Foundation/GTMNSThread+Blocks.h b/Foundation/GTMNSThread+Blocks.h index 16cf4e7..b5df094 100644 --- a/Foundation/GTMNSThread+Blocks.h +++ b/Foundation/GTMNSThread+Blocks.h @@ -47,6 +47,7 @@ @interface GTMSimpleWorkerThread : NSThread { @private CFRunLoopRef runLoop_; + NSConditionLock *runLock_; } // Will stop the thread. diff --git a/Foundation/GTMNSThread+Blocks.m b/Foundation/GTMNSThread+Blocks.m index cb5243d..f835a0b 100644 --- a/Foundation/GTMNSThread+Blocks.m +++ b/Foundation/GTMNSThread+Blocks.m @@ -58,8 +58,26 @@ typedef int (*pthread_setname_np_Ptr)(const char*); #if GTM_IPHONE_SDK || (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) +enum { + GTMSimpleThreadIsStarting = 1, + GTMSimpleThreadIsFinished +}; + @implementation GTMSimpleWorkerThread +- (id)init { + if ((self = [super init])) { + runLock_ = + [[NSConditionLock alloc] initWithCondition:GTMSimpleThreadIsStarting]; + } + return self; +} + +- (void)dealloc { + [runLock_ release]; + [super dealloc]; +} + - (void)setThreadDebuggerName:(NSString *)name { // [NSThread setName:] doesn't actually set the name in such a way that the // debugger can see it. So we handle it here instead. @@ -70,6 +88,10 @@ typedef int (*pthread_setname_np_Ptr)(const char*); } - (void)main { + _GTMDevAssert([runLock_ condition] == GTMSimpleThreadIsStarting, + @"Bad start condition %d", [runLock_ condition]); + [runLock_ lockWhenCondition:GTMSimpleThreadIsStarting]; + [self setThreadDebuggerName:[self name]]; // Add a port to the runloop so that it stays alive. Without a port attached @@ -82,10 +104,19 @@ typedef int (*pthread_setname_np_Ptr)(const char*); // runloops making it impossible to stop. runLoop_ = [loop getCFRunLoop]; CFRunLoopRun(); + [runLock_ unlockWithCondition:GTMSimpleThreadIsFinished]; } - (void)stop { CFRunLoopStop(runLoop_); + if (![[NSThread currentThread] isEqual:self]) { + // If we are calling stop from a separate thread, we block until the + // simple thread actually leaves the runloop so there is no race condition + // between the current thread and the simple thread. In effect it's a + // join operation. + [runLock_ lockWhenCondition:GTMSimpleThreadIsFinished]; + [runLock_ unlockWithCondition:GTMSimpleThreadIsFinished]; + } } - (void)setName:(NSString *)name { -- cgit v1.2.3