blob: 3ef4a455b127ebdc8fa7a8ad23e28ecb22f776ce [file] [log] [blame]
//
// GTMNSThread+Blocks.m
//
// Copyright 2012 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy
// of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
//
#import "GTMNSThread+Blocks.h"
#import <pthread.h>
#import <dlfcn.h>
#if NS_BLOCKS_AVAILABLE
@implementation NSThread (GTMBlocksAdditions)
+ (void)gtm_runBlockOnCurrentThread:(void (^)(void))block {
block();
}
- (void)gtm_performBlock:(void (^)(void))block {
if ([[NSThread currentThread] isEqual:self]) {
block();
} else {
[self gtm_performWaitingUntilDone:NO block:block];
}
}
- (void)gtm_performWaitingUntilDone:(BOOL)waitDone block:(void (^)(void))block {
[NSThread performSelector:@selector(gtm_runBlockOnCurrentThread:)
onThread:self
withObject:[[block copy] autorelease]
waitUntilDone:waitDone];
}
+ (void)gtm_performBlockInBackground:(void (^)(void))block {
[NSThread performSelectorInBackground:@selector(gtm_runBlockOnCurrentThread:)
withObject:[[block copy] autorelease]];
}
@end
#endif // NS_BLOCKS_AVAILABLE
@implementation GTMSimpleWorkerThread
- (void)main {
NSRunLoop *nsRunLoop = [NSRunLoop currentRunLoop];
// According to the NSRunLoop docs, a port must be added to the
// runloop to keep the loop alive, otherwise when you call
// runMode:beforeDate: it will immediately return with NO. We never
// send anything over this port, it's only here to keep the run loop
// looping.
[nsRunLoop addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
while (true) {
if (self.isCancelled) {
break;
}
BOOL ranLoop = [nsRunLoop runMode:NSDefaultRunLoopMode
beforeDate:[NSDate distantFuture]];
if (!ranLoop) {
break;
}
}
}
- (void)cancel {
[super cancel];
if (![[NSThread currentThread] isEqual:self]) {
// This call just forces the runloop in main to spin allowing main to see
// that the isCancelled flag has been set. Note that this is only really
// needed if there are no blocks/selectors in the queue for the thread. If
// there are other items to be processed in the queue, the next one will be
// executed and then the "cancel" will be seen in main, and it will exit
// (and the other blocks will be dropped).
[self performSelector:@selector(class)
onThread:self
withObject:nil
waitUntilDone:NO];
}
}
- (void)stop {
if ([[NSThread currentThread] isEqual:self]) {
[super cancel];
} else {
// This call forces the runloop in main to spin allowing main to see that
// the isCancelled flag has been set. Note that we explicitly want to send
// it to the thread to process so it is added to the end of the queue of
// blocks to be processed. 'stop' guarantees that all items in the queue
// will be processed before it ends.
[self performSelector:@selector(cancel)
onThread:self
withObject:nil
waitUntilDone:YES];
while (![self isFinished] || [self isExecuting]) {
// Spin until the thread is really done.
usleep(10);
}
}
}
@end