Nested callback to main thread from a dispatch queue not working? - objective-c

__block int x = 5;
dispatch_queue_t abe = dispatch_queue_create([[NSString stringWithFormat:#"abe'sQueue.%#", self] UTF8String], NULL);
dispatch_sync(abe, ^{
NSLog(#"%#", [self returnSomething]);
dispatch_async(dispatch_get_main_queue(), ^{
x = 20;
});
});
NSLog(#"%d", x);
The above always results in 5 being the logged value of 'x', even though the nested callback to the main thread changes the value asynchronously. Any ideas as to where I'm going wrong?

You are dispatching asynchronously to the main queue. That block is enqueued and the dispatch_async() returns immediately. Assuming the rest of the code is running on the main thread, then the asynchronously enqueued block won't be run until the next pass through the main event loop, which won't happen until after all of your code above executes.
For setting a scalar variable in that fashion, there is no reason to dispatch_async() to the main thread. Just set the variable directly (as the main thread is blocked on the dispatch_sync().

Related

dispatch_sync not working with main queue

please consider following code:
dispatch_queue_t myQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t myGroup = dispatch_group_create();
dispatch_group_async(myGroup, myQueue, ^{
NSLog(#"First block of code");
});
dispatch_group_async(myGroup, myQueue, ^{
NSLog(#"Second block of code");
});
dispatch_group_async(myGroup, myQueue, ^{
dispatch_sync(dispatch_get_main_queue(), ^{ //problem here
dispatch_time_t myTime = dispatch_time(DISPATCH_TIME_NOW, 10ull * NSEC_PER_SEC);
dispatch_after(myTime, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(#"Third block of code work");
});
});
});
dispatch_group_wait(myGroup, DISPATCH_TIME_FOREVER);
NSLog(#" All done ?");
Problem is in dispatch_sync(dispatch_get_main_queue(), ^{
When i make dispatch_async(dispatch_get_main_queue(), ^{, it works as it should. Point of making that sync, is blocking main thread for 10 seconds, simulating "hard work". But, unfortunately, its not work. I wonder why?
What i want is: dispatch_sync block main thread for only 10 seconds, then execution continues.
What in fact happening: dispatch sync block whole application from any further execution.
If that code is running on the main event loop, then the dispatch_group_wait() is going to block the main queue. That prevents the synchronous execution of any block on the main queue and leads to deadlock.
You can verify this by pausing into the debugger. You'll likely see the main thread/queue blocked on the call to wait and a secondary thread/queue blocked on dispatch_sync.
Your code is presumably run on the main queue. It dispatches three blocks on the background, then waits until all three blocks have finished running. Since this is done on the main queue, other blocks will only start running on the main queue afterwards, that is after all the three async blocks that you queued up have finished.
Now the third of these three blocks calls the main thread with a sync call. What that means is it adds a block to the main queue, then it waits until the block starts executing, then it waits further until the block finishes executing, then the dispatch_sync call returns to its caller.
Well, that block dispatched to the main thread cannot start executing, because the main thread is busy waiting for the three async blocks to finish, and one of them won't finish because it is waiting for the block dispatched to the main thread to start executing, which it can't because and so on and so on forever. It doesn't actually matter one bit what you are trying to do in the synchronous block, because that block never gets as far as starting to execute.

dispatch_sync() always execute block in main thread

Is there any difference between if dispatch_sync is called in 3 different queue like
1.
dispatch_sync(dispatch_get_main_queue(),^(void){
NSLog(#"this execute in main thread") // via [NSThread isMainThread]
});
2.
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(void){
NSLog(#"this also execute in main thread") // via [NSThread isMainThread]
}
3.
dispatch_queue_t queue;
queue = dispatch_queue_create("com.example.MyQueue", NULL);
dispatch_sync(queue, ^(void){
NSLog(#"this also execute in main thread") // via [NSThread isMainThread]
}
Whenever i call dispatch_sync, block executed in main thread, without considering in which queue it is dispatched. So why this function take queue as a argument as it doesn't use it. Can someone please clarify this?
dispatch_sync is a blocking operation. That is, the function will not return until the work represented in the block is completed.
When dispatched to an asynchronous queue -- like one of the global queues or a concurrent queue of your own making -- there is no reason to do anything but invoke the block on the thread that called dispatch_sync(). Even in the case of invoking the block on a synchronous queue, the dispatch_sync() is going to wait until completion anyway so, internally, it might as well stop until the rest of the work is done in the queue and then execute the block directly.
As it turns out, passing data from thread A to thread B is expensive. If the queue is in the state where execution can happen immediately, then dispatch_sync will fast path the execution by simply calling the block on the thread that dispatch_sync was called on.
And, by definition, you shouldn't care. The calling thread is blocked -- can't do a thing -- until dispatch_sync() returns.
So, really, all of this is an implementation detail. GCD is free to execute the blocks on whatever threads it deems most appropriate. It just so happens that don't context switch is often the most important rule of figuring that out.
See dispatch_sync documentation, which notes
As an optimization, this function invokes the block on the current thread when possible.
If you dispatch something synchronously, since the thread must wait for the dispatched code to complete, anyway, it will frequently run that code on the current thread. So if dispatched synchronously from the main thread, it will run on main thread. If dispatched synchronously from a background thread, it will run on that background thread.
As noted by ipmcc, a well-known exception is when a background thread dispatches something synchronously to the main thread. As the libdispatch source says:
It's preferred to execute synchronous blocks on the current thread
due to thread-local side effects, garbage collection, etc. However,
blocks submitted to the main thread MUST be run on the main thread.
For your problem: you call the dispatch_sync always in main queue, and if you wanna know why, see the following:
At first you need to pay attention to the description on of "dispatch_sync"
Submits a block to a dispatch queue for synchronous execution. Unlike dispatch_async,
this function does not return until the block has finished.
Calling this function and targeting the current QUEUE(NOT THREAD) results in deadlock.
#define logStep(step,queue) NSLog(#"step: %d at thread: %# in -- queue: %s",step,[NSThread currentThread],dispatch_queue_get_label(queue));
// call the method in main thread within viewDidLoad or viewWillAppear ...
- (void)testDispatchSync{
//let's distinctly tell the 4 queues we often use at first
self.concurrentQ = dispatch_queue_create("com.shared.concurrent", DISPATCH_QUEUE_CONCURRENT);
self.serialQ = dispatch_queue_create("com.shared.serial", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t mainQ = dispatch_get_main_queue();
dispatch_queue_t globalQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
logStep(1,mainQ) //we're in main thread and main queue, current queue IS main queue
// do a sync in main thread & concurrent queue
dispatch_sync(_concurrentQ, ^{
logStep(2,_concurrentQ)
});
// do a sync in main thread & serial queue
dispatch_sync(_serialQ, ^{
logStep(3,_serialQ)
});
//uncommenting the following code that you'wll see a crash will occur, because current queue is main queue
// dispatch_sync(mainQ, ^{
// logStep(4, mainQ)
// });
dispatch_async(_concurrentQ, ^{
// inside of the this scope, current queue is "_concurrentQ"
logStep(11,_concurrentQ)
// using sync in any queue here will be safe!
dispatch_sync(_concurrentQ, ^{
logStep(12,_concurrentQ)
});
dispatch_sync(_serialQ, ^{
logStep(13,_concurrentQ)
});
dispatch_sync(mainQ, ^{
logStep(14,mainQ)
});
dispatch_sync(globalQ, ^{
logStep(15,globalQ)
});
// using async in any queue here will be safe!
dispatch_async(_concurrentQ, ^{
logStep(111,_concurrentQ)
});
dispatch_async(_serialQ, ^{
logStep(112,_concurrentQ)
});
dispatch_async(mainQ, ^{
logStep(113,mainQ)
});
dispatch_async(globalQ, ^{
logStep(114,globalQ)
});
});
dispatch_async(_serialQ, ^{
// inside of the this scope, current queue is "_serialQ"
logStep(21,_serialQ)
// using async in any queue except current queue here will be safe!
dispatch_sync(_concurrentQ, ^{
logStep(22,_concurrentQ)
});
dispatch_sync(mainQ, ^{
logStep(23,mainQ)
});
dispatch_sync(globalQ, ^{
logStep(24,globalQ)
});
//uncommenting the following code that you'wll see a crash will occur, because current queue is "_serialQ"
// dispatch_sync(_serialQ, ^{ //app will die at here
// logStep(25,_serialQ)
// });
});
}
So we got the conclusion:
the key problem is that thread will be blocked while "dispatch_sync" running on current queue which is a serial queue at the same time.
main queue is also a serial queue so that it explained why you cann't call dispatch_sync in main thread

How to wait past dispatch_async before proceeding?

I have a series of dispatch_async that I am performing and I would like to only update the UI when they are all done. Problem is the method within dispatch_async calls something in a separate thread so it returns before the data is fully loaded and dispatch_group_notify is called before everything is loaded.
So I introduce a infinite loop to make it wait until a flag is set.
Is this the best way? See code below.
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_group_t group = dispatch_group_create();
for (...) {
dispatch_group_async(group, queue, ^{
__block BOOL dataLoaded = NO;
[thirdPartyCodeCallWithCompletion:^{
dataLoaded = YES;
}];
// prevent infinite loop
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)),
queue, ^{
dataLoaded = YES;
});
// infinite loop to wait until data is loaded
while (1) {
if (dataLoaded) break;
}
}
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
//update UI
});
}
You're already aware of dispatch groups. Why not just use dispatch_group_wait(), which includes support for a timeout? You can use dispatch_group_enter() and dispatch_group_leave() rather than dispatch_group_async() to make the group not done until the internal block for the third-party call with completion is finished.
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_group_t group = dispatch_group_create();
for (...) {
dispatch_group_enter(group);
dispatch_async(queue, ^{
[thirdPartyCodeCallWithCompletion:^{
dispatch_group_leave(group);
}];
}
}
dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, NSECS_PER_SEC));
dispatch_async(dispatch_get_main_queue(), ^{
//update UI
});
The use of dispatch_group_wait() does make this code synchronous, which is bad if run on the main thread. Depending on what exactly is supposed to happen if it times out, you could use dispatch_group_notify() as you were and use dispatch_after() to just updates the UI rather than trying to pretend the block completed.
Update: I tweaked my code to make sure that "update UI" happens on the main queue, just in case this code isn't already on the main thread.
By the way, I only used dispatch_async() for the block which calls thirdPartyCodeCallWithCompletion: because your original used dispatch_group_async() and I wasn't sure that the hypothetical method was asynchronous. Most APIs which take a completion block are asynchronous, though. If that one is, then you can just invoke it directly.
Another method is to use semaphore and the dispatch_semaphore_wait:
// Create your semaphore, 0 is specifying the initial pool size
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
#autoreleasepool {
// Your code goes here
}
// Release the resource and signal the semaphore
dispatch_semaphore_signal(semaphore);
});
// Wait for the above block execution, AKA Waits for (decrements) a semaphore.
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// After this line you can now safely assert anything you want regarding the async operation since it is done.

dipatch_async releases local variable

I did not find any suitable answers on the web, so I post my question here.
__block int test = 1;
dispatch_async(dispatch_get_main_queue(), ^{
test = 2;
});
NSLog(#"%i",test);
This code will result in console message "1".
__block NSString *test = #"no";
dispatch_async(dispatch_get_main_queue(), ^{
test = #"yes";
});
NSLog(#"%#",test);
This code will result in console message "no".
Why is it so? I thought that __block identifier should solve all problems in this case.
My hypothesis is that local variable was copied and code inside block hadn't actually modify anything outside itself.
How can I modify local variables inside dispatch_async?
Sorry if it is a noob question.
You are dispatching asynchronously to the main queue.
The dispatch_async returns before the block is executed (coincidentally).
To underscore how non-deterministic concurrent programming can be:
Note that your NSLog() might sometimes see the new value maybe once in a very blue moon. You might not ever see it in your debugging environment, but some customer might encounter that behavior 3 years from now on a system configuration that doesn't exist today.
To fix?
dispatch_sync() thereby ensuring that your background queue and the main queue are effectively acting like a less efficient single serial queue.
... or ...
Use some kind of synchronization construct to message from the main queue back to your local queue when the operation is done. I.e.:
dispatch_async(otherQueue, ^{
... do something ...;
dispatch_async(firstQueue, ^{
done(calculatedValue);
};
};
You are dispatching setting test asynchronously, meaning your NSLog statement is going to fire before the block.
You would have to do it this way, or change it to a dispatch_sync.
__block NSString *test = #"no";
dispatch_async(dispatch_get_main_queue(), ^{
test = #"yes";
NSLog(#"%#",test);
});
If you need to do something on a background thread and then pop it back onto the main thread, just do the following:
__block NSString *test = #"no";
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
test = #"setting Testing in the background";
NSLog(#"Printing from the background:%#",test);
dispatch_async(dispatch_get_main_queue(), ^{
test = #"Setting Test on the main thread";
NSLog(#"Logging test on the main thread:%#",test);
});
});
You are calling dispatch_async. Although you are executing the code on the main thread, it is still not determined wether the code "test = #"yes" gets executed before the "NSLog(#"%#",test);" code.
If you use dispatch_sync it will work as expexted.

Prevent a block from running until all previous queue items have completed

Is it possible to add a block to the end of the current queue, and make sure that this block gets called after all existing items in the queue?
The code below doesn't seem to work:
- (void)someTaskWillBeDoneOnThisThreadLater {
// The current scope is a delegate method of a library I'm using,
// and unfortunately the required task gets executed after this delegate
// method is called.
// wait for current queue to be done with everything, including the current scope
dispatch_async(dispatch_get_current_queue(), ^{
// After everything is done, then call the main thread
dispatch_async(dispatch_get_main_queue(), ^{
// Perform some task on main thread
});
});
}
EDIT:
The following code fixed the problem, but I really don't want to rely on 1 second delay. I rather find a better solution.
dispatch_async(dispatch_get_global_queue(0, 0), ^{
dispatch_async(dispatch_get_main_queue(), ^{
sleep(1);
// Perform something on main thread
});
});
I think problem is that you need private queue to get this work. This code:
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(#"FIRST");
dispatch_async(dispatch_get_current_queue(), ^{
NSLog(#"LAST");
dispatch_async(dispatch_get_main_queue(), ^{
// ...
});
});
sleep(2);
NSLog(#"SECOND");
});
Gives:
2012-07-31 22:57:20.005 Objective-C App[22526:1703] FIRST
2012-07-31 22:57:20.009 Objective-C App[22526:2003] LAST
2012-07-31 22:57:22.010 Objective-C App[22526:1703] SECOND
Which isn't what you wanted. Even dispatch_barrier_async won't help. But when you use private queue:
dispatch_queue_t queue = dispatch_queue_create("test", 0);
dispatch_async(queue, ^{
NSLog(#"FIRST");
dispatch_async(queue, ^{
NSLog(#"LAST");
dispatch_async(dispatch_get_main_queue(), ^{
// ...
});
});
sleep(2);
NSLog(#"SECOND");
});
Will give you result you wanted:
2012-07-31 23:04:41.882 Objective-C App[22564:1703] FIRST
2012-07-31 23:04:43.887 Objective-C App[22564:1703] SECOND
2012-07-31 23:04:43.889 Objective-C App[22564:1703] LAST
As you see "LAST" will be printed at the end and it'll wait 2 seconds until block already on queue is finished.
Although it's not clear that the actual problem is as you've described, there is a facility in GCD, available starting in OS X 10.7 or iOS 4.3 to achieve this: dispatch_barrier_async().
Submitting a Block to a queue using this function causes that Block to wait to execute until all previous Blocks have completed; likewise all subsequent Blocks wait for the barrier to complete.
Note that this is only relevant for concurrent queues, since the nature of a serial queue is that it will not start executing any Block until all previously submitted Blocks have completed.
From the dispatch_barrier_async documentation:
... When the barrier block reaches the front of a private concurrent
queue, it is not executed immediately. Instead, the queue waits until
its currently executing blocks finish executing. At that point, the
barrier block executes by itself. Any blocks submitted after the
barrier block are not executed until the barrier block completes.
So replacing dispatch_async by dispatch_barrier_async in your code should do what you want.
EDIT:
This works only on private (concurrent) queues, see also the #Johnnywho's answer and the following comments.
I overlooked that part of the documentation, sorry.