GCD, NSThread, and performSelector:onThread: issues - ios

I'm attempting to debug some iOS crash logs that contain the following error message:
*** Terminating app due to uncaught exception 'NSDestinationInvalidException', reason: '*** -[SomeClass
performSelector:onThread:withObject:waitUntilDone:modes:]: target
thread exited while waiting for the perform
The relevant section of the code is:
- (void) runInvocationOnMyThread:(NSInvocation*)invocation {
NSThread* currentThread = [NSThread currentThread];
if (currentThread != myThread) {
//call over to the correct thread
[self performSelector:#selector(runInvocationOnMyThread:) onThread:myThread withObject:invocation waitUntilDone:YES];
}
else {
//we're okay to invoke the target now
[invocation invoke];
}
}
This is similar to the issue discussed here, except that I'm not trying to cancel my onThread: thread. In fact, in my case onThread: is being passed a reference to the application's main thread, so it should not be possible for it to terminate unless the entire app is terminating.
So the first question is, is the "target" thread referred to in the error message the one I'm passing to onThread:, or the one that's waiting for the invocation to complete on the onThread: thread?
I've assumed that it's the second option, as if the main thread really has terminated the crash of the background thread is kind of moot anyways.
With that in mind, and based upon the following discussion from the reference docs for performSelector:onThread:...:
Special Considerations
This method registers with the runloop of its
current context, and depends on that runloop being run on a regular
basis to perform correctly. One common context where you might call
this method and end up registering with a runloop that is not
automatically run on a regular basis is when being invoked by a
dispatch queue. If you need this type of functionality when running on
a dispatch queue, you should use dispatch_after and related methods to
get the behavior you want.
...I've modified my code to prefer the use of GCD over performSelector:onThread:..., as follows:
- (void) runInvocationOnMyThread:(NSInvocation*)invocation {
NSThread* currentThread = [NSThread currentThread];
if (currentThread != myThread) {
//call over to the correct thread
if ([myThread isMainThread]) {
dispatch_sync(dispatch_get_main_queue(), ^{
[invocation invoke];
});
}
else {
[self performSelector:#selector(runInvocationOnMyThread:) onThread:myThread withObject:invocation waitUntilDone:YES];
}
}
else {
//we're okay to invoke the target now
[invocation invoke];
}
}
Which seems to work fine (though no idea if it fixes the crash, as it's an exceedingly rare crash). Perhaps someone can comment on whether this approach is more or less prone to crashing than the original?
Anyways, the main problem is that there's only an obvious way to use GCD when the target thread is the main thread. In my case, this is true, but I'd like to be able to use GCD regardless of whether or not the target thread is the main thread.
So the more important question is, is there a way to map from an arbitrary NSThread to a corresponding queue in GCD? Ideally something along the lines of dispatch_queue_t dispatch_get_queue_for_thread(NSThread* thread), so that I can revise my code to be:
- (void) runInvocationOnMyThread:(NSInvocation*)invocation {
NSThread* currentThread = [NSThread currentThread];
if (currentThread != myThread) {
//call over to the correct thread
dispatch_sync(dispatch_get_queue_for_thread(myThread), ^{
[invocation invoke];
});
}
else {
//we're okay to invoke the target now
[invocation invoke];
}
}
Is this possible, or is there not a direct mapping from NSThread to GCD queue that can be applied?

Given your stated goal of wrapping a 3rd party API that requires thread affinity, you might try something like using a forwarding proxy to ensure methods are only called on the correct thread. There are a few tricks to doing this, but I managed to whip something up that might help.
Let's assume you have an object XXThreadSensitiveObject with an interface that looks something like this:
#interface XXThreadSensitiveObject : NSObject
- (instancetype)init NS_DESIGNATED_INITIALIZER;
- (void)foo;
- (void)bar;
- (NSInteger)addX: (NSInteger)x Y: (NSInteger)y;
#end
And the goal is for -foo, -bar and -addX:Y: to always be called on the same thread.
Let's also say that if we create this object on the main thread, then our expectation is that the main thread is the blessed thread and all calls should be on the main thread, but that if it's created from any non-main thread, then it should spawn its own thread so it can guarantee thread affinity going forward. (Because GCD managed threads are ephemeral, there is no way to have thread affinity with a GCD managed thread.)
One possible implementation might look like this:
// Since NSThread appears to retain the target for the thread "main" method, we need to make it separate from either our proxy
// or the object itself.
#interface XXThreadMain : NSObject
#end
// This is a proxy that will ensure that all invocations happen on the correct thread.
#interface XXThreadAffinityProxy : NSProxy
{
#public
NSThread* mThread;
id mTarget;
XXThreadMain* mThreadMain;
}
#end
#implementation XXThreadSensitiveObject
{
// We don't actually *need* this ivar, and we're skankily stealing it from the proxy in order to have it.
// It's really just a diagnostic so we can assert that we're on the right thread in method calls.
__unsafe_unretained NSThread* mThread;
}
- (instancetype)init
{
if (self = [super init])
{
// Create a proxy for us (that will retain us)
XXThreadAffinityProxy* proxy = [[XXThreadAffinityProxy alloc] initWithTarget: self];
// Steal a ref to the thread from it (as mentioned above, this is not required.)
mThread = proxy->mThread;
// Replace self with the proxy.
self = (id)proxy;
}
// Return the proxy.
return self;
}
- (void)foo
{
NSParameterAssert([NSThread currentThread] == mThread || (!mThread && [NSThread isMainThread]));
NSLog(#"-foo called on %#", [NSThread currentThread]);
}
- (void)bar
{
NSParameterAssert([NSThread currentThread] == mThread || (!mThread && [NSThread isMainThread]));
NSLog(#"-bar called on %#", [NSThread currentThread]);
}
- (NSInteger)addX: (NSInteger)x Y: (NSInteger)y
{
NSParameterAssert([NSThread currentThread] == mThread || (!mThread && [NSThread isMainThread]));
NSLog(#"-addX:Y: called on %#", [NSThread currentThread]);
return x + y;
}
#end
#implementation XXThreadMain
{
NSPort* mPort;
}
- (void)dealloc
{
[mPort invalidate];
}
// The main routine for the thread. Just spins a runloop for as long as the thread isnt cancelled.
- (void)p_threadMain: (id)obj
{
NSThread* thread = [NSThread currentThread];
NSParameterAssert(![thread isMainThread]);
NSRunLoop* currentRunLoop = [NSRunLoop currentRunLoop];
mPort = [NSPort port];
// If we dont register a mach port with the run loop, it will just exit immediately
[currentRunLoop addPort: mPort forMode: NSRunLoopCommonModes];
// Just loop until the thread is cancelled.
while (!thread.cancelled)
{
[currentRunLoop runMode: NSDefaultRunLoopMode beforeDate: [NSDate distantFuture]];
}
[currentRunLoop removePort: mPort forMode: NSRunLoopCommonModes];
[mPort invalidate];
mPort = nil;
}
- (void)p_wakeForThreadCancel
{
// Just causes the runloop to spin so that the loop in p_threadMain can notice that the thread has been cancelled.
}
#end
#implementation XXThreadAffinityProxy
- (instancetype)initWithTarget: (id)target
{
mTarget = target;
mThreadMain = [[XXThreadMain alloc] init];
// We'll assume, from now on, that if mThread is nil, we were on the main thread.
if (![NSThread isMainThread])
{
mThread = [[NSThread alloc] initWithTarget: mThreadMain selector: #selector(p_threadMain:) object:nil];
[mThread start];
}
return self;
}
- (void)dealloc
{
if (mThread && mThreadMain)
{
[mThread cancel];
const BOOL isCurrent = [mThread isEqual: [NSThread currentThread]];
if (!isCurrent && !mThread.finished)
{
// Wake it up.
[mThreadMain performSelector: #selector(p_wakeForThreadCancel) onThread:mThread withObject: nil waitUntilDone: YES modes: #[NSRunLoopCommonModes]];
}
}
mThreadMain = nil;
mThread = nil;
}
- (NSMethodSignature*)methodSignatureForSelector:(SEL)selector
{
NSMethodSignature *sig = [[mTarget class] instanceMethodSignatureForSelector:selector];
if (!sig)
{
sig = [NSMethodSignature signatureWithObjCTypes:"#^v^c"];
}
return sig;
}
- (void)forwardInvocation:(NSInvocation*)invocation
{
if ([mTarget respondsToSelector: [invocation selector]])
{
if ((!mThread && [NSThread isMainThread]) || (mThread && [mThread isEqual: [NSThread currentThread]]))
{
[invocation invokeWithTarget: mTarget];
}
else if (mThread)
{
[invocation performSelector: #selector(invokeWithTarget:) onThread: mThread withObject: mTarget waitUntilDone: YES modes: #[ NSRunLoopCommonModes ]];
}
else
{
[invocation performSelectorOnMainThread: #selector(invokeWithTarget:) withObject: mTarget waitUntilDone: YES];
}
}
else
{
[mTarget doesNotRecognizeSelector: invocation.selector];
}
}
#end
The ordering here is a little wonky, but XXThreadSensitiveObject can just do its work. XXThreadAffinityProxy is a thin proxy that does nothing other than ensuring that the invocations are happening on the right thread, and XXThreadMain is just a holder for the subordinate thread's main routine and some other minor mechanics. It's essentially just a workaround for a retain cycle that would otherwise be created between the thread and the proxy which has philosophical ownership of the thread.
The thing to know here is that threads are a relatively heavy abstraction, and are a limited resource. This design assumes that you're going to make one or two of these things and that they will be long lived. This usage pattern makes sense in the context of wrapping a 3rd party library that expects thread affinity, since that would typically be a singleton anyway, but this approach won't scale to more than a small handful of threads.

To your first Q:
I think the thread, sending the message is meant. But I cannot explain how this can happen.
Second: I would not mix NSThread and GCD. I think that there will be more problems than solutions. This is because of your last Q:
Each block is running on one thread. At least this is done, because thread migration for a block would be expensive. But different blocks in a queue can be distributed to many threads. This is obvious for parallel queues, but true for serial, too. (And have seen this in practice.)
I recommend to move your whole code to GCD. Once you are convenient with it, it is very easy to use and less error prone.

There is no mapping at all between queues and threads, with the sole exception of the main queue which always runs on the main thread. Any queue which targets the main queue will, of course, also run on the main thread. Any background queue can run on any thread, and can change thread from one block execution to the next. This is equally true for serial queues and for concurrent queues.
GCD maintains a thread pool which gets used for executing blocks according to the policies determined by the queue to which the block belongs. You are not supposed to know anything about those particular threads.

Related

How do I use an NSRunLoop on an NSOperationQueue?

I have an app which communicates with an ExternalAccessory over Bluetooth, there is some delay in responses so I want the IO to happen on a background thread.
I setup an NSOperationQueue for single-threaded operation to enqueue my requests:
self.sessionQueue = [NSOperationQueue new];
self.sessionQueue.maxConcurrentOperationCount = 1;
If I schedule reads and writes to the EAAccessory streams from that queue, my app crashes because data from the socket can't be delivered without an NSRunLoop on the thread that the queue is using. Immediately after initializing the queue, I create a run loop with an empty NSMachPort to keep it running and start it:
[self.sessionQueue addOperationWithBlock:^{
NSRunLoop* queueLoop = [NSRunLoop currentRunLoop];
[queueLoop addPort:[NSMachPort port] forMode:NSRunLoopCommonModes];
[queueLoop run]; // <- this blocks
}];
This blocks the queue as the run loop will never exit, but I'm not sure how to correctly manage the run loop so that I can successfully read from the accessory streams.
You shouldn't try to run a run loop inside an NSOperation. Grand Central Dispatch owns the thread on which the operation is running. You should start your own thread and use its run loop for your session streams.
However, you need to be aware that NSRunLoop is not generally thread safe, but CFRunLoop is. This means that you need to drop down to the CFRunLoop level when you want to run a block on your session-handling thread.
Also, the only way to get a reference to a background thread's run loop is to run something on that background thread. So step one is to create your own NSThread subclass that exports its own run loop:
typedef void (^MyThreadStartCallback)(CFRunLoopRef runLoop);
#interface MyThread: NSThread
/// After I'm started, I dispatch to the main queue to call `callback`,
// passing my runloop. Then I destroy my reference to `callback`.
- (instancetype)initWithCallback:(MyThreadStartCallback)callback;
#end
#implementation MyThread {
MyThreadStartCallback _callback;
}
- (instancetype)initWithCallback:(MyThreadStartCallback)callback {
if (self = [super init]) {
_callback = callback;
}
return self;
}
- (void)main {
CFRunLoopRef runLoop = CFRunLoopGetCurrent();
dispatch_async(dispatch_get_main_queue(), ^{
_callback(runLoop);
});
_callback = nil;
CFRunLoopRun();
}
#end
Now you can create an instance of MyThread, passing in a callback. When you start MyThread, it will make that callback run back on the main thread, and it will pass its own (MyThread's) run loop to the callback. So you can use a MyThread as your session-handling thread, like this:
#implementation Thing {
CFRunLoopRef _sessionRunLoop;
}
- (void)scheduleStreamsOfSession:(EASession *)session {
MyThread *thread = [[MyThread alloc] initWithCallback:^(CFRunLoopRef runLoop) {
// Here I'm on the main thread, but the session-handling thread has
// started running and its run loop is `runLoop`.
[self scheduleStreamsOfSession:session inRunLoop:runLoop];
}];
[thread start];
}
- (void)scheduleStreamsOfSession:(EASession *)session inRunLoop:(CFRunLoopRef)runLoop {
// Here I'm on the main thread. I'll save away the session-handling run loop
// so I can run more blocks on it later, perhaps to queue data for writing
// to the output stream.
_sessionRunLoop = runLoop;
NSInputStream *inputStream = session.inputStream;
NSOutputStream *outputStream = session.outputStream;
// Here I'm on the main thread, where it's not safe to use the
// session-handling thread's NSRunLoop, so I'll send a block to
// the session-handling thread.
CFRunLoopPerformBlock(runLoop, kCFRunLoopCommonModes, ^{
// Here I'm on the session-handling thread, where it's safe to
// use NSRunLoop to schedule the streams.
NSRunLoop *currentRunLoop = [NSRunLoop currentRunLoop];
[inputStream scheduleInRunLoop:currentRunLoop forMode:NSRunLoopCommonModes];
[outputStream scheduleInRunLoop:currentRunLoop forMode:NSRunLoopCommonModes];
});
// CFRunLoopPerformBlock does **not** wake up the run loop. Since I want
// to make sure the block runs as soon as possible, I have to wake up the
// run loop manually:
CFRunLoopWakeUp(_sessionRunLoop);
}
#end
Any thread can have an NSRunLoop created for it if needed, the main thread of any Cocoa or AppKit application has one running by default and any secondary threads must run them programmatically. If you were spawning an NSThread the thread body would be responsible for starting the NSRunLoop but an NSOperationQueue creates it's own thread or threads and dispatches operations to them.
When using an API which expects an NSRunLoop to deliver events to and from a background thread, either of your own creation, or one that libdispatch has created, you are responsible for making sure the NSRunLoop is run. Typically you will want to run the loop until some condition is met in each of your NSBlockOperation tasks, I wrote a category on NSRunLoop which simplifies this:
#import <Foundation/Foundation.h>
#interface NSRunLoop (Conditional)
-(BOOL)runWhileCondition:(BOOL *)condition inMode:(NSString *)mode inIntervals:(NSTimeInterval) quantum;
#end
#pragma mark -
#implementation NSRunLoop (Conditional)
-(BOOL)runWhileCondition:(BOOL *)condition inMode:(NSString *)mode inIntervals:(NSTimeInterval) quantum {
BOOL didRun = NO;
BOOL shouldRun = YES;
NSPort *dummyPort = [NSMachPort port];
[self addPort:dummyPort forMode:NSDefaultRunLoopMode];
while (shouldRun) {
#autoreleasepool {
didRun = [self runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:quantum]];
shouldRun = (didRun ? *condition : NO);
}
}
[self removePort:dummyPort forMode:NSDefaultRunLoopMode];
return didRun;
}
#end
With this condition you can schedule an NSBlockOperation which will start the run loop and run until the specified condition is NO:
__block BOOL streamOperationInProgress = YES;
[self.sessionQueue addOperationWithBlock:^{
NSRunLoop *queueLoop = [NSRunLoop currentRunLoop];
NSStream *someStream = // from somewhere...
[someStream setDelegate:self];
[someStream scheduleInRunLoop:queueLoop forMode:NSDefaultRunLoopMode]:
// the delegate implementation of stream:handleEvent:
// sets streamOperationInProgress = NO;
[queueLoop
runWhileCondition:&streamOperationInProgress
inMode:NSDefaultRunLoopMode
inIntervals:0.001];
}];
The wrinkle in the above example is putting the BOOL someplace that the delegate can set it to NO when the operation is complete.
Here's a gist of the NSRunLoop+Condition category.

iOS performSelectorInBackground inside performSelectorInBackground

It seems that the function call [self updateUI]; blocked by boo.
Is boo run in another background thread or same as foo as the code below?
How can the [self updateUI]; not block by boo?
- (void)MainFunction
{
[self performSelectorInBackground#selector(foo) withObject:nil];
}
- (void)foo
{
[self performSelectorInBackground#selector(boo) withObject:nil];
//updaate UI in MainThread
[self updateUI];
}
- (void)boo
{
//function here take long time to run;
}
In your code seems that you call foo in background and so the UI is updated in the background thread that is not possible because you need to do that in the main thread. In any case, performSelectorInBackground is a little bit old...use the dispatcher in this way:
- (void)MainFunction
{
[self foo];
}
- (void)foo
{
dispatch_async(dispatch_get_global_queue(DISPATCH_PRIORITY_DEFAUL, 0ull), ^{
[self boo];
dispatch_async(dispatch_get_main_queue(), ^{
//updaate UI in MainThread
[self updateUI];
};
};
}
- (void)boo
{
//function here take long time to run;
}
In this case updateUI wait boo, but if you want updateUI before and doesn't matter when boo finish:
- (void)foo
{
dispatch_async(dispatch_get_global_queue(DISPATCH_PRIORITY_DEFAUL, 0ull), ^{
[self boo];
};
[self updateUI];
}
performSelectorInBackground performs the selector on a NEW thread. From Apple docs:
This method creates a new thread in your application, putting your
application into multithreaded mode if it was not already. The method
represented by aSelector must set up the thread environment just as
you would for any other new thread in your program.
If you would like to perform both functions on the SAME background thread, you'll have to declare the background thread (also called queue) as a private member of the class (so it will be accessible from both functions) and perform the selector on that queue

How to dispatch code blocks to the same thread in iOS?

Main aspect of the question: It's about iOS. Can I somehow dispatch code blocks in a way, that they will all (a) run in background and (b) on the same thread? I want to run some time-consuming operations in background, but these have to be run on the same thread, because they involve resources, that mustn't be shared among threads.
Further technical details, if required: It's about implementing an sqlite plugin for Apache Cordova, a framework for HTML5 apps on mobile platforms. This plugin should be an implementation of WebSQL in the means of the Cordova's plugin API. (That means, it's not possible to wrap entire transactions within single blocks, what could make everything easier.)
Here is some code from Cordova's Docs:
- (void)myPluginMethod:(CDVInvokedUrlCommand*)command
{
// Check command.arguments here.
[self.commandDelegate runInBackground:^{
NSString* payload = nil;
// Some blocking logic...
CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:payload];
// The sendPluginResult method is thread-safe.
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}];
}
But as far as I know, there is no guarantee, that those dispatched code blocks (see runInBackground) will run on the same thread.
GCD makes no guarantee that two blocks run on the same thread, even if they belong to the same queue (with the exception of the main queue, of course). However, if you're using a serial queue (DISPATCH_QUEUE_SERIAL) this isn't a problem as you then know that there is no concurrent access to your data.
The man page for dispatch_queue_create says:
Queues are not bound to any specific thread of execution and blocks submitted to independent queues may execute concurrently.
I'm not aware of any way to bind a queue to a specific thread (after all, not needing to care about threads is a major point of GCD). The reason why you can use a serial queue without worrying about the actual thread is this promise:
All memory writes performed by a block dispatched to a serial queue are guaranteed to be visible to subsequent blocks dispatched to the same queue.
That is, a memory barrier seems to be used.
When dealing with threading issues, your main concern is usually to avoid that two threads access something concurrently. If you're using a serial queue you do not have this problem. It usually doesn't really matter which thread is accessing your resources. For example, we're using serial queues to manage Core Data access without a problem.
Edit:
It seems you really found a rare case where you need to be working on the same thread. You could implement your own worker thread:
Prerequisites:
A NSMutableArray (let's call it blockQueue).
A NSCondition (let's call it queueCondition).
Create a new NSThread.
The thread's method has an endless loop in which it locks the condition, waits for it if the queue is empty (and the "quit" bool is false), dequeues a block and executes it.
A method that locks the condition an enqueues the block.
Due to the condition, the thread will simply sleep while there's no work to do.
So, roughly (untested, assuming ARC):
- (void)startWorkerThread
{
workerThread = [[NSThread alloc]
initWithTarget:self
selector:#selector(threadMain)
object:nil
];
[workerThread start];
}
- (void)threadMain
{
void (^block)();
NSThread *currentThread;
currentThread = [NSThread currentThread];
while (1) {
[queueCondition lock];
{
while ([blockQueue count] == 0 && ![currentThread isCancelled]) {
[queueCondition wait];
}
if ([currentThread isCancelled]) {
[queueCondition unlock];
return;
}
block = [blockQueue objectAtIndex:0];
[blockQueue removeObjectAtIndex:0];
}
[queueCondition unlock];
// Execute block outside the condition, since it's also a lock!
// We want to give other threads the possibility to enqueue
// a new block while we're executing a block.
block();
}
}
- (void)enqueue:(void(^)())block
{
[queueCondition lock];
{
// Copy the block! IIRC you'll get strange things or
// even crashes if you don't.
[blockQueue addObject:[block copy]];
[queueCondition signal];
}
[queueCondition unlock];
}
- (void)stopThread
{
[queueCondition lock];
{
[workerThread cancel];
[queueCondition signal];
}
[queueCondition unlock];
}
Untested Swift 5 port:
var workerThread: Thread?
var blockQueue = [() -> Void]()
let queueCondition = NSCondition()
func startWorkerThread() {
workerThread = Thread() {
let currentThread = Thread.current
while true {
self.queueCondition.lock()
while self.blockQueue.isEmpty && !currentThread.isCancelled {
self.queueCondition.wait()
}
if currentThread.isCancelled {
self.queueCondition.unlock()
return
}
let block = self.blockQueue.remove(at: 0)
self.queueCondition.unlock()
// Execute block outside the condition, since it's also a lock!
// We want to give other threads the possibility to enqueue
// a new block while we're executing a block.
block()
}
}
workerThread?.start()
}
func enqueue(_ block: #escaping () -> Void) {
queueCondition.lock()
blockQueue.append(block)
queueCondition.signal()
queueCondition.unlock()
}
func stopThread() {
queueCondition.lock()
workerThread?.cancel()
queueCondition.signal()
queueCondition.unlock()
}
In GCD: no, that's not possible with the current lib dispatch.
Blocks can be executed by dispatch lib on whatever thread which is available, no matter to which queue they have been dispatched.
One exception is the main queue, which always executes its blocks on the main thread.
Please file a feature request to Apple, since it seems justified and sound. But I fear it's not feasible, otherwise it would already exist ;)
You can use NSOperationQueue. You can make it use just one thread by using method - (void)setMaxConcurrentOperationCount:(NSInteger)count. Set it to 1
Create a serial dispatch queue, and dispatch all the calls to that serial dispatch queue. All the calls will be performed in the background, but sequentially on the same thread.
If you want to perform a selector in Main Thread, you can use
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait
and if you want to it to perform in background thread
- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)object
and if you want to perform in any other thread use GCD(Grand Central Dispatch)
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
//code to be executed on the main queue after delay
});
You can either use NSOperationQueue with a MaxConcurrentOperationCount of 1 or go the manual way down the road by using NSThread model (rather than Grand Central Dispatch).
Using the latter I would recommend you to implement a worker-method which runs in a thread and pulls work-packages (or commands) from a queue or a pool that is being feed from outside of the thread. Just make sure you use Locks / Mutex / Synchronisation.
Never tried this but this might do the trick. Use separate properties of atomic dispatch queues for each operation.
#property (strong, atomic) dispatch_queue_t downloadQueue;
Queue/Thread 1 for first operation
downloadQueue = dispatch_queue_create("operation1", NULL);
etc.
Since atomic is thread safe, downloadQueue should not be accessed by other threads. So it makes sure that there will be only single thread per operation and other threads will not access it.
Just like this,
dispatch_asyn(dispatch_get_current_queue, ^ {
});

ios using release in an async block

I have an IOS project (ARC disabled) which has several view controllers. One particular controller initialises a member object of type MyClass, however when the view controller is dismissed, I'm calling a cleanup method for the object which uses a thread (using dispatch-async) to make some time consuming operations and then when these operations are done im executing a [self release] on the main queue for the object. Is this a good practise, will it cause any errors? Below is a similar example to what im doing:
#implementation ViewController
- (void)viewDidLoad
{
myObj = [[MyClass alloc] init];
}
-(void)viewWillDisappear
{
[myObj cleanup];
}
#end
//myClass
#implementation MyClass
- (void)cleanup()
{
dispatch_queue_t myQueue = dispatch_queue_create ("MyClassDeallocQueue", NULL);
dispatch_async(myQueue, ^{
//time consuming operations
dispatch_async(dispatch_get_main_queue(), ^{
[self release];
});
});
}
#end
Is this a good practise, will it cause any errors?
Currently, your code has an unbalanced retain/release. That is definitely an error (over release).
"Is it good practice?" - well, I don't know what you are trying to accomplish. But if your goal is to keep self alive, until after the block is executed, it is already accomplished purely through the fact that self will be captured. So, strictly a release is not needed.
However, if you NOT explicitly release self on the main thread, you introduce a subtle bug: it might happen that the block has the last reference to self, and since it may execute on some arbitrary thread, it will release self on this non-main thread. And this is forbidden: UIKit methods (including dealloc) MUST be called on the main thread!
Thus, it might make sense:
[self retain];
dispatch_async(myQueue, ^{
// time consuming operation, which captures `self`
[self doSomething];
...
// ensure that `dealloc` will be executed on the main thread, if
// last reference is held by the block:
dispatch_async(dispatch_get_main_queue(), ^{
[self release];
});
});
or shorter:
dispatch_async(myQueue, ^{
// time consuming operation, which captures `self`
[self doSomething];
...
// ensure that `dealloc` will be executed on the main thread, if
// last reference is held by the block:
dispatch_async(dispatch_get_main_queue(), ^{
[self self];
});
});
Edit:
It's an interesting question, whether the "short" version is actually tread-safe or has a race:
Suppose, self will be released in the block executed on myQueue, as the effect of capturing self before it will be retained in the same bock as an effect of capturing self for the block executed on the main queue. Then, we have an issue. Comments appreciated.

Exit a NSThread when it's sleeping?

I am creating a new Thread which runs one of my method:
Now what i am doing is as follows:
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:#selector(myThreadFunc) object:nil];
[thread start];
in myThreadFunc
{
while(isRunning){
[self updateSomething];
[NSThread sleepForTimeInterval:3.0];
}
NSLog(#"out");
}
In another func,i set isRunning = NO and thread = nil or [thread cancel] but myThreadFunc is sleeping so thread cannot exit.
How can i control this case?
Thanks so much.
Don't use a thread. Use a timer. If the something is expensive, dispatch it off to some queue other than the main queue and set some state variable to show it's still running (if the something isn't meant to run concurrently). Then, just cancel your timer. A simple example of the timer call-back function might be:
- (void)doSomething:(NSTimer*)timer
{
// this assumes that this "something" only ever
// runs once at a time no matter what, adjust this
// to an ivar if it's per-class instance or something
static BOOL alreadyDoingSomething = NO;
if( alreadyDoingSomething ) return;
alreadyDoingSomething = YES;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self updateSomething];
alreadyDoingSomething = NO;
});
}
Now, if you simply cancel the timer, this will stop running. When you're ready to start it again, schedule a new timer with this method as the designated selector. To make this behave similar to your example above, you might set the timer interval to three seconds.

Resources