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.
Related
I am trying to use dispatch_queue_set_specific() and dispatch_get_specific() in combination in method to check if the current queue is same as the target queue. But since this method can be called from multiple threads, I need to ensure thread safety. So, my question is are these methods thread safe. If not, how can I ensure thread safety here?
I am setting a data using dispatch_queue_set_specific() on the target queue and using dispatch_get_specific() to compare the data on current queue, if they are same I am on the same queue.
static inline (BOOL)is_current_queue(dispatch_queue_t queue) {
int key, data;
dispatch_queue_set_specific(queue, &key, &data, nil);
if (dispatch_get_specific(&key) == &data) {
return YES;
}
return NO;
}
Are dispatch_get_specific() & dispatch_queue_set_specific() thread safe?
It would seem likely for routines in a concurrency library but the documentation isn't explicit on the point – just be thankful Apple actually provide any documentation at all, most if it is now relegated to their "documentation archive" ;-(
Fortunately for you libdispatch is open source and a check of the source confirms that they are – they lock around the critical parts.
HTH
BTW lines 4-8 of your code snippet are better written:
return dispatch_get_specific(&key) == &data;
In objective C,
I am making my program to wait using while loop
doInitialize()
{
dispach_group_t loadDataGroup=dispatch_group_create();
dispatch_group_async(loadDataGroup,...get_global_queue(..),0),^{
renewauth();
}
dispatch_group_notify(loadDataGroup,...get_global_queue(..),0),^{
//Do other tasks once renew session has completed...
}
}
renewauth()
{
RenewAuthTokenInProgress=true;
startRenewThread();
**while (RenewAuthTokenInProgress);**
}
In turn startRenewThread() function also performs dispatch_async operation inside. So I have to make renewAuth() wait.
And async task in startRenewThread will update the bool variable once renewal is successful.
Is there any better approach of doing it other than dispatch_groups?
And is it good to make other threads wait with while (true) statement?
Manoj Kumar,
using a while loop to wait till the boolean variable change is not the correct approach to solve the problem. Here are few of the issues with this method
Your CPU is un-necessarily burdened with checking the variable regularly.
This will clearly show that developer isn't much equipted with basic skills of coding and features available with language.
If for any reason your variable will never change then your CPU will never stop checking the value of bool in while loop and blocks the execution of further code on the same thread.
Here are few of the correct approach :
Blocks or closures : Make use of blocks to execute the code asynchronously when the RenewAuthToken is done.
Delegates : if blocks are harder to understand, Make use of delegates and trigger the delegate when you are done with RenewAuthToken.
Notifications : Add observer for notifications in classes which needs to respond when RenewAuthToken is done and throw notification from the asynctask and let the class to catch it execute the code.
Locks : If it is necessary to block the execution of the thread till the response comes use locks to control the thread execution rather than using while loop
EDIT
As pointed out by fogmeister in comments
If you block the main thread for too long with a while(true) loop then
the app will actually be terminated by the iOS Watchdog as it will
assume it has crashed
Please have a look at the link : understand iOS watchdog termination reasons provided by fogmeister
Hope it helps.
I believe what you need it's a semaphore like:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
__block BOOL done = FALSE;
while (true) {
[self someCompletionMethod completion:^(BOOL success) {
if(success) { // Stop condition
done = TRUE;
}
// do something
dispatch_semaphore_signal(sem); // This will let a new iteration
}];
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
if(done) {
dispatch_async(dispatch_get_main_queue(), ^{
// Dispatch to main
NSLog(#"Done!");
break;
});
}
}
});
Semaphores are an old-school threading concept introduced to the world by the ever-so-humble Edsger W. Dijkstra. Semaphores are a complex topic because they build upon the intricacies of operating system functions.
You can see a tutorial here about semaphore and check it out more links: https://www.raywenderlich.com/63338/grand-central-dispatch-in-depth-part-2
I hope this can help you.
What you do is absolutely lethal. It blocks the running thread (presumably the main thread) so the UI is frozen. It runs one core at 100% load for no reason whatsoever which empties the battery rapidly and heats up the phone. This will get you some very, very unhappy customers or very, very happy ex-customers.
Anything like this has to run in the background: startRenewThread should trigger some action that sets RenewAuthTokenInProgress = NO and sets whether there is a new token or not, and then triggers further action.
This is an absolutely essential programming pattern on iOS (and Android as far as I know).
Excuse me if this question sounds stupid but this is beyond my knowledge of Objective-C.
I am developing some classes that have to always be executed on the main thread.
Ok, I can pollute my code with a bunch of
dispatch_async(dispatch_get_main_queue(),
^{
});
but I would like to know if there is something I can do to prevent the methods of this class from running on other threads that is not the main or to at least warn during debugging, compiling, or whatever if they are used not on the main thread.
thanks
I sprinkle such methods with my BLOCK_UI() macro from https://github.com/gradha/ELHASO-iOS-snippets. At runtime the macro will assert if the method is not running on the main thread. The macro goes away in release builds because I consider calling such an API in the background a programmer error, but if you want to make an API which is permissive with the programmer, you can also check for the main thread and invoke yourself in the main thread if needed. Example:
if ([NSThread isMainThread]) {
[self do_request:url];
} else {
[self performSelectorOnMainThread:#selector(do_request:)
withObject:url waitUntilDone:NO];
}
You can always check via the helpful API "[NSThread isMainThread]"
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;
}
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.