// // 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 #import #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