dispatch_sync on main queue hangs in unit test - objective-c

I was having some trouble unit testing some grand central dispatch code with the built in Xcode unit testing framework, SenTestingKit. I managed to boil my problem done to this. I have a unit test that builds a block and tries to execute it on the main thread. However, the block is never actually executed, so the test hangs because it's a synchronous dispatch.
- (void)testSample {
dispatch_sync(dispatch_get_main_queue(), ^(void) {
NSLog(#"on main thread!");
});
STFail(#"FAIL!");
}
What is it about the testing environment that causes this to hang?

dispatch_sync runs a block on a given queue and waits for it to complete. In this case, the queue is the main dispatch queue. The main queue runs all its operations on the main thread, in FIFO (first-in-first-out) order. That means that whenever you call dispatch_sync, your new block will be put at the end of the line, and won't run until everything else before it in the queue is done.
The problem here is that the block you just enqueued is at the end of the line waiting to run on the main thread—while the testSample method is currently running on the main thread. The block at the end of the queue can't get access to the main thread until the current method (itself) finishes using the main thread. However dispatch_sync means Submits a block object for execution on a dispatch queue and waits until that block completes.

The problem in your code is that no matter whether you use dispatch_sync or dispatch_async , STFail() will always be called, causing your test to fail.
More importantly, as BJ Homer's explained, if you need to run something synchronously in the main queue, you must make sure you are not in the main queue or a dead-lock will happen. If you are in the main queue you can simply run the block as a regular function.
Hope this helps:
- (void)testSample {
__block BOOL didRunBlock = NO;
void (^yourBlock)(void) = ^(void) {
NSLog(#"on main queue!");
// Probably you want to do more checks here...
didRunBlock = YES;
};
// 2012/12/05 Note: dispatch_get_current_queue() function has been
// deprecated starting in iOS6 and OSX10.8. Docs clearly state they
// should be used only for debugging/testing. Luckily this is our case :)
dispatch_queue_t currentQueue = dispatch_get_current_queue();
dispatch_queue_t mainQueue = dispatch_get_main_queue();
if (currentQueue == mainQueue) {
blockInTheMainThread();
} else {
dispatch_sync(mainQueue, yourBlock);
}
STAssertEquals(YES, didRunBlock, #"FAIL!");
}

If you are on the main queue and synchronously wait for the main queue to be available you will indeed wait a long time. You should test to make sure you are not already on the main thread.

Will you ever get out of house if you must wait for yourself to get out house first? You guessed right! No! :]
Basically if:
You are on FooQueue. (doesn't have to be main_queue)
You call the method using sync ie in a serial way and want to execute on FooQueue.
It will never happen for same reason that you will never get out of house!
It won't ever get dispatched because it's waiting for itself to get off the queue!

To follow up, since
dispatch_get_current_queue()
is now deprecated, you can use
[NSThread isMainThread]
to see if you are on the main thread.
So, using the other answer above, you could do:
- (void)testSample
{
BOOL __block didRunBlock = NO;
void (^yourBlock)(void) = ^(void) {
NSLog(#"on main queue!");
didRunBlock = YES;
};
if ([NSThread isMainThread])
yourBlock();
else
dispatch_sync(dispatch_get_main_queue(), yourBlock);
STAssertEquals(YES, didRunBlock, #"FAIL!");
}

Related

ObjectiveC - Avoiding deadlock while synchronous dispatch to main queue from background

Recently I came to a point where I needed some block of code to execute always on the main thread synchronously. This block can be called from any thread. I solved this problem with the code that was already suggested in this SO answer by #Brad Larson
As the comments to this answer it is evident that the deadlock can occur, but I got into the deadlock very very easily. Please have a look at this code.
-(IBAction) buttonClicked
{
// Dispatch on the global concurrent queue async.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSString* data = [self getTheString];
NSLog(#"From Background Thread: %#", data);
};
// Dispatch on the main queue async.
dispatch_async(dispatch_get_main_queue(), ^{
NSString* data = [self getTheString];
NSLog(#"From Main Thread: %#", data);
};
}
// This method can be called from any thread so synchronize it.
// Also the code that sets the string variable based on some logic need to execute on main thread.
-(NSString*) getTheString
{
__block NSString* data = nil;
#synchronized(self)
{
// Have some code here that need to be synchronized between threads.
// .......
//
// Create a block to be executed on the main thread.
void (^blockToBeRunOnMainThread)(void) = ^{
// This is just a sample.
// Determining the actual string value can be more complex.
data = #"Tarun";
};
[self dispatchOnMainThreadSynchronously:blockToBeRunOnMainThread];
}
}
- (void) dispatchOnMainThreadSynchronously:(void(^)(void))block
{
if([NSThread isMainThread])
{
if (block)
{
block();
}
}
else
{
dispatch_sync(dispatch_get_main_queue(), ^{
if (block)
{
block();
}
});
}
}
In this piece of code there are two simultaneous asynchronous requests to function getTheString (Assume you have no control over the buttonClicked method and how it calls getTheString api) . Suppose the request from global queue comes first and it is trying to run the block on the main thread synchronously, till that time background thread in waiting for main thread to execute the block synchronously, at the same time request from main queue comes and trying the acquire the lock from background thread, but as background thread in not complete main thread waiting for background thread to complete. Here we have a deadlock on main thread as main thread waiting for background thread to finish, and background thread is waiting for main thread to execute block.
If I remove the #synchronize statement everything works fine as expected. May be I don't need a #synchronize statement here but in same case you may need to have this. Or it can even happen from some other parts of the code.
I tried to search the whole net for the solution and also tried dispatch_semaphore but couldn't solve the issue. May be I am just not doing things the right way.
I assume this is classic problem of deadlock and faced by developers again and again, and probably have solved it to some extent. Can anyone help with this, or point me to right direction?
I would create a synchronous queue (NSOperationQueue would be simplest) and submit the block to be run on the main thread to that queue. The queue would dispatch the blocks in the order received, maintaining the ordering you desire. At the same time, it disassociates the synchronicity between calling the getTheString method and the dispatch to the main thread.

dispatch_async doesn't fire until returned parent thread returns?

My Question is - Does dispatch_async fire immediately? I'm seeing that the creating thread has to return before dispatch_async fires.
My issue is that I am trying to make a async call to a 3rd party library appear to be synchronous. I know, I know... I should allow these async operations run as intended but I'm trying to do this way down in a complex multi-threaded operation and I really don't have a choice.
here's what starts the async call
-(void)connect
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0) , ^{
[device connect];
});
while(!connectCompleted)
{
NSLog(#"Sleeping..");
[NSThread sleepForTimeInterval:1.0];
}
}
and here's the delegate function that gets called AFTER the [device connect] successfully connects:
- (void)didConnect:(BXPrinterController *)controller
printer:(BXPrinter *)printer
{
connectCompleted = YES;
NSLog(#"didConnect");
}
with that dispatch_async wrapped around [device connect] the delegate never gets fired;
Does dispatch_async fire immediately
Of course not! That is what async means! The whole basis of the concept "asynchronous" (which is what async stands for) is that this is code that runs at some future unspecified time. Meanwhile the code that calls dispatch_async continues on to its end without waiting.
If for whatever reason you must make a call appear synchronous, I'd recommend that you first implement it synchronously (because you'll end up doing that anyway if people had enough of delays), and then you write a helper method that is synchronous:
Step 1: Create a dispatch_semaphore_t.
Step 2: Start the asynchronous code, with every code path ending by sending a signal to the dispatch_semaphore_t.
Step 3: Send a wait to the dispatch_semaphore_t. It wakes up, with no CPU time spent on it at all, just when the asynchronous code finishes.

Why is this dispatch_sync() call freezing?

I'm using the Kiwi testing framework to test an authentication method in my app. The test freezes at a call to dispatch_sync which looks like this:
dispatch_queue_t main = dispatch_get_main_queue();
dispatch_sync(main, ^
{
[[NSNotificationCenter defaultCenter] postNotificationName:kNotificationAuthenticationSuccess object:nil userInfo:ret];
});
I'd like to know why it freezes there, if anyone has any hints.
For the second part of your question regarding the hint on the freeze:
When calling dispatch_sync on a queue, always verify that this queue is not already the current queue (dispatch_get_current_queue()). Because dispatch_sync will queue your block on the queue passed as the first parameter, and then will wait for this block to be executed before continuing.
So if the dispatch_get_current_queue() and the queue on which you enqueue your block are the same, namely the main queue in your case, the main queue will block on the call to dispatch_sync until… the main queue as executed the block, but it can't, as the queue is blocked, and you have a beautiful deadlock here.
One solution ([EDIT] up until iOS6):
dispatch_queue_t main = dispatch_get_main_queue();
dispatch_block_t block = ^
{
[[NSNotificationCenter defaultCenter] postNotificationName:kNotificationAuthenticationSuccess object:nil userInfo:ret];
};
if (dispatch_get_current_queue() == main)
block(); // execute the block directly, as main is already the current active queue
else
dispatch_sync(main, block); // ask the main queue, which is not the current queue, to execute the block, and wait for it to be executed before continuing
[EDIT] Be careful, dispatch_get_current_queue() is only to be used for debugging purposes and never in production. In fact, dispatch_get_current_queue is deprecated since iOS6.1.3.
If you are in the specific case of the main queue (which is associated with the main thread only), you may instead test [NSThread isMainThread] as suggested by #meaning-matters.
By the way, are you sure you need to dispatch_sync in your case?
I guess sending your notification a bit later, avoiding to block until it has been sent, is acceptable in your case, so you may also consider using dispatch_async (instead of using dispatch_sync and needing the queue comparison condition), which would avoid the deadlock issue too.
dispatch_get_current_queue() is deprecated starting from iOS 6, and dispatch_get_current_queue() == dispatch_get_main_queue() was found to be false while on main thread on iOS 6.1.3.
In iOS 6 and beyond simply do:
dispatch_block_t block = ^
{
<your code here>
};
if ([NSThread isMainThread])
{
block();
}
else
{
dispatch_sync(dispatch_get_main_queue(), block);
}

Protecting critical code from being called again

I need to protect a critical area of my code, which is multi-threaded. I want to prevent it from being called multiple times before the other thread is finished. This is what I am working with:
- (void) filterAllEventsIntoDictionary{
// start critical area
if (self.sortedKeys.count != 0) {
[self.sortedKeys removeAllObjects];
}
dispatch_async(self.filterMainQueue, ^{
[self internal_filterAllEventsIntoDictionary];
dispatch_sync(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
});
}
Since the internal_filterAllEventsIntoDictionary method also accesses self.sortedKeys, if this code is called twice, it crashes because of removeAllObjects at the start.
I still need to call the internal... method in another thread since I don't want to block the UI. So what's the best way to block on the start of this method while the dispatch_async call is still not finished?
While I am far from being a concurrency expert, it sounds to me like you need a lock on your sortedKeys object. If you used a traditional lock, though, you'd end up blocking the main thread.
The recommended replacement for locks in the world of Grand Central Dispatch is to put critical sections of code on a serial queue. See "Eliminating Lock-Based Code" in the Concurrency Programming Guide.
If you put the [self.sortedKeys removeAllObjects]; call onto the same queue that the block with the internal... call is scheduled on, you guarantee that it won't happen until after that block completes:
// start critical area
dispatch_async(self.filterMainQueue, ^{
if (self.sortedKeys.count != 0) {
[self.sortedKeys removeAllObjects];
}
});
This assumes that filterMainQueue is serial. Using dispatch_async for the critical section ensures that the main thread will not be blocked. Also note the warning in "Dispatch Queues and Thread Safety":
Do not call the dispatch_sync function from a task that is executing on the same queue that you pass to your function call. Doing so will deadlock the queue.
Although this will only be an issue if the internal... method does something that causes this method to be called again.

GCD to perform task in main thread

I have a callback which might come from any thread. When I get this callback, then I would like to perform a certain task on the main thread.
Do I need to check whether I already am on the main thread - or is there any penalty by not performing this check befora calling the code below?
dispatch_async(dispatch_get_main_queue(), ^{
// do work here
});
No, you do not need to check whether you’re already on the main thread. By dispatching the block to the main queue, you’re just scheduling the block to be executed serially on the main thread, which happens when the corresponding run loop is run.
If you already are on the main thread, the behaviour is the same: the block is scheduled, and executed when the run loop of the main thread is run.
For the asynchronous dispatch case you describe above, you shouldn't need to check if you're on the main thread. As Bavarious indicates, this will simply be queued up to be run on the main thread.
However, if you attempt to do the above using a dispatch_sync() and your callback is on the main thread, your application will deadlock at that point. I describe this in my answer here, because this behavior surprised me when moving some code from -performSelectorOnMainThread:. As I mention there, I created a helper function:
void runOnMainQueueWithoutDeadlocking(void (^block)(void))
{
if ([NSThread isMainThread])
{
block();
}
else
{
dispatch_sync(dispatch_get_main_queue(), block);
}
}
which will run a block synchronously on the main thread if the method you're in isn't currently on the main thread, and just executes the block inline if it is. You can employ syntax like the following to use this:
runOnMainQueueWithoutDeadlocking(^{
//Do stuff
});
As the other answers mentioned, dispatch_async from the main thread is fine.
However, depending on your use case, there is a side effect that you may consider a disadvantage: since the block is scheduled on a queue, it won't execute until control goes back to the run loop, which will have the effect of delaying your block's execution.
For example,
NSLog(#"before dispatch async");
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"inside dispatch async block main thread from main thread");
});
NSLog(#"after dispatch async");
Will print out:
before dispatch async
after dispatch async
inside dispatch async block main thread from main thread
For this reason, if you were expecting the block to execute in-between the outer NSLog's, dispatch_async would not help you.
No you don't need to check if you're in the main thread. Here is how you can do this in Swift:
runThisInMainThread { () -> Void in
runThisInMainThread { () -> Void in
// No problem
}
}
func runThisInMainThread(block: dispatch_block_t) {
dispatch_async(dispatch_get_main_queue(), block)
}
Its included as a standard function in my repo, check it out: https://github.com/goktugyil/EZSwiftExtensions