GCD deprecated methods - ios

I used to have an helper method (static) that ensures that a completion block is called on the correct queue.
+ (void)_deadlockCheckBlock:(void(^)(void))block caller:(dispatch_queue_t)caller {
NSParameterAssert(block);
NSParameterAssert(caller);
if (caller == dispatch_get_current_queue()) {
block();
}
else {
dispatch_async(caller, block);
}}
Now to overcome the deprecation of
dispatch_get_current_queue()
I've rewritten the method using the get_main_queue method.
+ (void)_deadlockCheckBlock:(void(^)(void))block caller:(dispatch_queue_t)caller {
NSParameterAssert(block);
NSParameterAssert(caller);
dispatch_sync(dispatch_get_main_queue(), ^{
//This will ensure to be on the main queue
if (caller == dispatch_get_main_queue()) {
block();
}
else {
dispatch_async(caller, block);
}
});}
Is there a better way to get the same behaviour without going on the main queue?

The problem with that (and the reason get_current_queue is deprecated) is that there can be many current queues. If you dispatch_sync from one queue to another, both are now "current" in the sense that dispatch_sync to either of them will deadlock. Same with dispatch_set_target_queue.
The reliable ways to avoid deadlock are to always use dispatch_async (or other async APIs; dispatch_group_notify for example), to avoid reentrant control flow, or to pass along enough additional information to decide properly (if using a queue as a lock you could have an 'already locked' flag that you pass along for example).
In general, code that requires recursive locks or things that simulate them (like using the current queue to decide whether to dispatch_sync) is code that may have some invariant (the one the lock is protecting) broken and is expected to work anyway, which is a somewhat scary notion.

Related

Can i use GCD for background thread and performSelector for resume back to main thread? [duplicate]

I've used both GCD and performSelectorOnMainThread:waitUntilDone in my apps, and tend to think of them as interchangeable--that is, performSelectorOnMainThread:waitUntilDone is an Obj-C wrapper to the GCD C syntax. I've been thinking of these two commands as equivalent:
dispatch_sync(dispatch_get_main_queue(), ^{ [self doit:YES]; });
[self performSelectorOnMainThread:#selector(doit:) withObject:YES waitUntilDone:YES];
Am I incorrect? That is, is there a difference of the performSelector* commands versus the GCD ones? I've read a lot of documentation on them, but have yet to see a definitive answer.
As Jacob points out, while they may appear the same, they are different things. In fact, there's a significant difference in the way that they handle sending actions to the main thread if you're already running on the main thread.
I ran into this recently, where I had a common method that sometimes was run from something on the main thread, sometimes not. In order to protect certain UI updates, I had been using -performSelectorOnMainThread: for them with no problems.
When I switched over to using dispatch_sync on the main queue, the application would deadlock whenever this method was run on the main queue. Reading the documentation on dispatch_sync, we see:
Calling this function and targeting
the current queue results in deadlock.
where for -performSelectorOnMainThread: we see
wait
A Boolean that specifies whether the
current thread blocks until after the
specified selector is performed on the
receiver on the main thread. Specify
YES to block this thread; otherwise,
specify NO to have this method return
immediately.
If the current thread is also the main
thread, and you specify YES for this
parameter, the message is delivered
and processed immediately.
I still prefer the elegance of GCD, the better compile-time checking it provides, and its greater flexibility regarding arguments, etc., so I made this little helper function to prevent deadlocks:
void runOnMainQueueWithoutDeadlocking(void (^block)(void))
{
if ([NSThread isMainThread])
{
block();
}
else
{
dispatch_sync(dispatch_get_main_queue(), block);
}
}
Update: In response to Dave Dribin pointing out the caveats section ondispatch_get_current_queue(), I've changed to using [NSThread isMainThread] in the above code.
I then use
runOnMainQueueWithoutDeadlocking(^{
//Do stuff
});
to perform the actions I need to secure on the main thread, without worrying about what thread the original method was executed on.
performSelectorOnMainThread: does not use GCD to send messages to objects on the main thread.
Here's how the documentation says the method is implemented:
- (void) performSelectorOnMainThread:(SEL) selector withObject:(id) obj waitUntilDone:(BOOL) wait {
[[NSRunLoop mainRunLoop] performSelector:selector target:self withObject:obj order:1 modes: NSRunLoopCommonModes];
}
And on performSelector:target:withObject:order:modes:, the documentation states:
This method sets up a timer to perform the aSelector message on the current thread’s run loop at the start of the next run loop iteration. The timer is configured to run in the modes specified by the modes parameter. When the timer fires, the thread attempts to dequeue the message from the run loop and perform the selector. It succeeds if the run loop is running and in one of the specified modes; otherwise, the timer waits until the run loop is in one of those modes.
GCD's way is suppose to be more efficient and easier to handle and is only available in iOS4 onwards whereas performSelector is supported in the older and newer iOS.

Heavy calculations on a background thread in getter

I need to perform a lot of calculations every time a getter is called from my app. The data returned from the getter is constantly changing based on the environment, and it has to do a lot of calculations to compute what it should return. Therefore, I don't want the code in the getter running on the main thread. This is what I have so far:
#interface Calculator ()
#property (nonatomic, strong) dispatch_queue_t calculationThread;
#end
- (dispatch_queue_t)calculationThread {
if (!_calculationThread) {
_calculationThread = dispatch_queue_create("calculation_thread", NULL);
}
return _calculationThread;
}
- (NSArray *)calculation {
// perform calculation in calculationThread, which should not be on main thread and be asynchronous
return arrayContainingCalculations;
}
I basically want to know how to use GCD to replace the comment. I have tried using dispatch_queue_t and dispatch_group_notify, but I don't seem to be implementing it correctly.
I think using a callback is probably the simplest and most efficient solution to this problem.
It is simply impossible to use only a single getter to do an asynchronous calculation without blocking the thread it was called on, as you expect code called after it to continue executing while it does the calculation.
You just have to create a new method with a callback, for example:
-(void) doCalculation:(void(^)(NSArray* result))callback {
dispatch_async(self.calculationQueue, ^{
NSArray* result = self.calculation; // make sure this is doing a synchronous calculation. If it's asynchronous, you'll have to use a semaphore (or another callback!).
if (callback) {
dispatch_async(dispatch_get_main_queue(), ^{ // return to main thread
callback(result);
});
}
});
}
Then you can simply invoke it on your main thread like so:
[calculator doCalculation:^(NSArray* result) {
textView.text = [result[0] stringValue]; // update UI with new info.
}];
That way you can easily keep your resulting code in-line with the call to the method.
It's also worth noting that your calculationQueue's getter (I renamed it, as the word thread is misleading when you're working with queues) isn't thread-safe. I would advise you use a dispatch_once to make it thread-safe:
-(dispatch_queue_t) calculationQueue {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_calculationQueue = dispatch_queue_create("calculation_queue", DISPATCH_QUEUE_SERIAL);
});
return _calculationQueue;
}
You can use the following to put it on your queue asynchronously. The problem however is that the method is going to return immediately.
dispatch_async(your_queue, ^{
// Code to be executed on background thread
});
What you probably want is to have some kind of method calculateWithCompletion where the caller can define a block that you can invoke once the completion is finished.
As you said in your comment to Peter, you want to keep it so you can call self.calculation and get your logic executed and return the calculation synchronously.
However because you want to avoid locking the UI while this logic is executing, you would like it to execute on a background thread.
Therefore, all you should need to do is use dispatch_sync instead of dispatch_async inside of your calculate method.
What dispatch_sync does is it places a task (the block that contains your logic) onto a specified queue (probably should pick a global concurrent queue), which then executes your task on a thread the OS picks for you (not the main thread). dispatch_async does the same, Except that dispatch_async will continue execution immediately after dispatching your task onto a queue.
dispatch_sync on the other hand, will block execution in the current run loop until your tasks returns.
This will allow you to execute your expensive logic on a background thread, while still remaining synchronous so that you can continue using self.calculation

is there a way to still get which queue I am on instead of dispatch_get_current_queue? [duplicate]

Recently, I had the need for a function that I could use to guarantee synchronous execution of a given block on a particular serial dispatch queue. There was the possibility that this shared function could be called from something already running on that queue, so I needed to check for this case in order to prevent a deadlock from a synchronous dispatch to the same queue.
I used code like the following to do this:
void runSynchronouslyOnVideoProcessingQueue(void (^block)(void))
{
dispatch_queue_t videoProcessingQueue = [GPUImageOpenGLESContext sharedOpenGLESQueue];
if (dispatch_get_current_queue() == videoProcessingQueue)
{
block();
}
else
{
dispatch_sync(videoProcessingQueue, block);
}
}
This function relies on the use of dispatch_get_current_queue() to determine the identity of the queue this function is running on and compares that against the target queue. If there's a match, it knows to just run the block inline without the dispatch to that queue, because the function is already running on it.
I've heard conflicting things about whether or not it was proper to use dispatch_get_current_queue() to do comparisons like this, and I see this wording in the headers:
Recommended for debugging and logging purposes only:
The code must not make any assumptions about the queue returned,
unless it is one of the global queues or a queue the code has itself
created. The code must not assume that synchronous execution onto a
queue is safe from deadlock if that queue is not the one returned by
dispatch_get_current_queue().
Additionally, in iOS 6.0 (but not yet for Mountain Lion), the GCD headers now mark this function as being deprecated.
It sounds like I should not be using this function in this manner, but I'm not sure what I should use in its place. For a function like the above that targeted the main queue, I could use [NSThread isMainThread], but how can I check if I'm running on one of my custom serial queues so that I can prevent a deadlock?
Assign whatever identifier you want using dispatch_queue_set_specific(). You can then check your identifier using dispatch_get_specific().
Remember that dispatch_get_specific() is nice because it'll start at the current queue, and then walk up the target queues if the key isn't set on the current one. This usually doesn't matter, but can be useful in some cases.
This is a very simple solution. It is not as performant as using dispatch_queue_set_specific and dispatch_get_specific manually – I don't have the metrics on that.
#import <libkern/OSAtomic.h>
BOOL dispatch_is_on_queue(dispatch_queue_t queue)
{
int key;
static int32_t incrementer;
CFNumberRef value = CFBridgingRetain(#(OSAtomicIncrement32(&incrementer)));
dispatch_queue_set_specific(queue, &key, value, nil);
BOOL result = dispatch_get_specific(&key) == value;
dispatch_queue_set_specific(queue, &key, nil, nil);
CFRelease(value);
return result;
}

How can I verify that I am running on a given GCD queue without using dispatch_get_current_queue()?

Recently, I had the need for a function that I could use to guarantee synchronous execution of a given block on a particular serial dispatch queue. There was the possibility that this shared function could be called from something already running on that queue, so I needed to check for this case in order to prevent a deadlock from a synchronous dispatch to the same queue.
I used code like the following to do this:
void runSynchronouslyOnVideoProcessingQueue(void (^block)(void))
{
dispatch_queue_t videoProcessingQueue = [GPUImageOpenGLESContext sharedOpenGLESQueue];
if (dispatch_get_current_queue() == videoProcessingQueue)
{
block();
}
else
{
dispatch_sync(videoProcessingQueue, block);
}
}
This function relies on the use of dispatch_get_current_queue() to determine the identity of the queue this function is running on and compares that against the target queue. If there's a match, it knows to just run the block inline without the dispatch to that queue, because the function is already running on it.
I've heard conflicting things about whether or not it was proper to use dispatch_get_current_queue() to do comparisons like this, and I see this wording in the headers:
Recommended for debugging and logging purposes only:
The code must not make any assumptions about the queue returned,
unless it is one of the global queues or a queue the code has itself
created. The code must not assume that synchronous execution onto a
queue is safe from deadlock if that queue is not the one returned by
dispatch_get_current_queue().
Additionally, in iOS 6.0 (but not yet for Mountain Lion), the GCD headers now mark this function as being deprecated.
It sounds like I should not be using this function in this manner, but I'm not sure what I should use in its place. For a function like the above that targeted the main queue, I could use [NSThread isMainThread], but how can I check if I'm running on one of my custom serial queues so that I can prevent a deadlock?
Assign whatever identifier you want using dispatch_queue_set_specific(). You can then check your identifier using dispatch_get_specific().
Remember that dispatch_get_specific() is nice because it'll start at the current queue, and then walk up the target queues if the key isn't set on the current one. This usually doesn't matter, but can be useful in some cases.
This is a very simple solution. It is not as performant as using dispatch_queue_set_specific and dispatch_get_specific manually – I don't have the metrics on that.
#import <libkern/OSAtomic.h>
BOOL dispatch_is_on_queue(dispatch_queue_t queue)
{
int key;
static int32_t incrementer;
CFNumberRef value = CFBridgingRetain(#(OSAtomicIncrement32(&incrementer)));
dispatch_queue_set_specific(queue, &key, value, nil);
BOOL result = dispatch_get_specific(&key) == value;
dispatch_queue_set_specific(queue, &key, nil, nil);
CFRelease(value);
return result;
}

iOS Threading Violations Detection?

On iOS, I am wondering if there is anything akin to the NSZombies flag that can be set to detect/raise when a call is made to an API from a background thread when the main thread is required.
With blocks, it seems to be easy to get into a situation where one either makes false assumptions or mistakes about which thread is doing what. A debugging flag that could detect when a call is made from the wrong thread would be really useful for finding these cases.
It is primarily Apple's frameworks that concern me (Cocoa Touch).
Filed a bug report, #12180446.
Use the following in any API of yours that requires being on the main thread:
NSAssert([NSThread isMainThread], #"This must only be called on the main thread.");
Apple does not provide such a thing that covers all of UIKit. You will need to call it when you need it.
I do something more practical. when I call blocks. I do it with a custom method.
+ (void) ensureDispatchOfBlock:(dispatch_block_t) block onQueue:(dispatch_queue_t) queue async:(BOOL) async{
if (queue == nil || dispatch_get_current_queue() == queue){
block();
}
else {
if (async){
dispatch_async(queue, block);
}
else {
dispatch_sync(queue, block);
}
}
}
+ (void) ensureDispatchOnMainThread:(dispatch_block_t) block async:(BOOL) async{
[self ensureDispatchOfBlock:block onQueue:dispatch_get_main_queue() async:async];
}
This allows me to "Ensure" the dispatch of the block being provided is going to happen on the block it was intended.
Just remember to use async:NO very sparingly.

Resources