In iOS, we have GCD and Operation to handle concurrent programming.
looking into GCD we have QoS classes, and they're simple and straight forward, this question is about why DispatchQueue.main.async is commonly used to asynchronies X tasks in the Main Thread.
So when we usually handle updating something in the UI we usually use that function since to prevent any irresponsiveness from the application.
makes me think is writing code inside the UIViewController usually executed in the main thread ?
but also knowing that callback & completionHandler usually execute without specifying on what thread they are in, and the UI never had a problem with that !! so it is on the background ?
How Swift handles this ? and what thread am i writing on by default without specifying anything ?
Since there are more than one question here, let's attempt to answer them one by one.
why DispatchQueue.main.async is commonly used to asynchronies X tasks
in the Main Thread.
Before mentioning a direct answer, make sure that you don't have confusion of understanding:
Serial <===> Concurrent.
Sync <===> Async.
Keep in mind that DispatchQueue.main is serial queue. Using sync or async has nothing to do with determining serialization or currency of a queue, instead they refer to how the task is handled. Thus saying DispatchQueue.main.async means that:
returns control to the current queue right after task has been sent to
be performed on the different queue. It doesn't wait until the task is
finished. It doesn't block the queue.
cited from: https://stackoverflow.com/a/44324968/5501940 (I'd recommend to check it.)
In other words, async means: this will happen on the main thead and update it when it is finished. That's what makes what you said:
So when we usually handle updating something in the UI we usually use
that function since to prevent any irresponsiveness from the
application.
seems to be sensible; Using sync -instead of async- will block the main.
makes me think is writing code inside the UIViewController usually
executed in the main thread ?
First of all: By default, without specifying which thread should execute a chunk of code it would be the main thread. However your question seems to be unspecific because inside a UIViewController we can call functionalities that are not executed on the main thread by specifying it.
but also knowing that callback & completionHandler usually execute
without specifying on what thread they are in, and the UI never had a
problem with that !! so it is on the background ?
"knowing that callback & completionHandler usually execute without specifying on what thread they are in" No! You have to specify it. A good real example for it, actually that's how Main Thread Checker works.
I believe that there is something you are missing here, when dealing when a built-in method from the UIKit -for instance- that returns a completion handler, we can't see that it contains something like DispatchQueue.main.async when calling the completion handler; So, if you didn't execute the code inside its completion handler inside DispatchQueue.main.async so we should assume that it handles it for you! It doesn't mean that it is not implemented somewhere.
Another real-world example, Alamofire! When calling
Alamofire.request("https://httpbin.org/get").responseJSON { response in
// what is going on here work has to be async on the main thread
}
That's why you can call it without facing any "hanging" issue on the main thread; It doesn't mean its not handled, instead it means they handle it for you so you don't have to worry about it.
I need a clarifications on how dispatch_queues is related to reentrancy and deadlocks.
Reading this blog post Thread Safety Basics on iOS/OS X, I encountered this sentence:
All dispatch queues are non-reentrant, meaning you will deadlock if
you attempt to dispatch_sync on the current queue.
So, what is the relationship between reentrancy and deadlock? Why, if a dispatch_queue is non-reentrant, does a deadlock arise when you are using dispatch_sync call?
In my understanding, you can have a deadlock using dispatch_sync only if the thread you are running on is the same thread where the block is dispatch into.
A simple example is the following. If I run the code in the main thread, since the dispatch_get_main_queue() will grab the main thread as well and I will end in a deadlock.
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(#"Deadlock!!!");
});
Any clarifications?
All dispatch queues are non-reentrant, meaning you will deadlock if
you attempt to dispatch_sync on the current queue.
So, what is the relationship between reentrancy and deadlock? Why, if
a dispatch_queue is non-reentrant, does a deadlock arise when you are
using dispatch_sync call?
Without having read that article, I imagine that statement was in reference to serial queues, because it's otherwise false.
Now, let's consider a simplified conceptual view of how dispatch queues work (in some made-up pseudo-language). We also assume a serial queue, and don't consider target queues.
Dispatch Queue
When you create a dispatch queue, basically you get a FIFO queue, a simple data structure where you can push objects on the end, and take objects off the front.
You also get some complex mechanisms to manage thread pools and do synchronization, but most of that is for performance. Let's simply assume that you also get a thread that just runs an infinite loop, processing messages from the queue.
void processQueue(queue) {
for (;;) {
waitUntilQueueIsNotEmptyInAThreadSaveManner(queue)
block = removeFirstObject(queue);
block();
}
}
dispatch_async
Taking the same simplistic view of dispatch_async yields something like this...
void dispatch_async(queue, block) {
appendToEndInAThreadSafeManner(queue, block);
}
All it is really doing is taking the block, and adding it to the queue. This is why it returns immediately, it just adds the block onto the end of the data structure. At some point, that other thread will pull this block off the queue, and execute it.
Note, that this is where the FIFO guarantee comes into play. The thread pulling blocks off the queue and executing them always takes them in the order that they were placed on the queue. It then waits until that block has fully executed before getting the next block off the queue
dispatch_sync
Now, another simplistic view of dispatch_sync. In this case, the API guarantees that it will wait until the block has run to completion before it returns. In particular, calling this function does not violate the FIFO guarantee.
void dispatch_sync(queue, block) {
bool done = false;
dispatch_async(queue, { block(); done = true; });
while (!done) { }
}
Now, this is actually done with semaphores so there is no cpu loops and boolean flag, and it doesn't use a separate block, but we are trying to keep it simple. You should get the idea.
The block is placed on the queue, and then the function waits until it knows for sure that "the other thread" has run the block to completion.
Reentrancy
Now, we can get a reentrant call in a number of different ways. Let's consider the most obvious.
block1 = {
dispatch_sync(queue, block2);
}
dispatch_sync(queue, block1);
This will place block1 on the queue, and wait for it to run. Eventually the thread processing the queue will pop block1 off, and start executing it. When block1 executes, it will put block2 on the queue, and then wait for it to finish executing.
This is one meaning of reentrancy: when you re-enter a call to dispatch_sync from another call to dispatch_sync
Deadlock from reentering dispatch_sync
However, block1 is now running inside the queue's for loop. That code is executing block1, and will not process anything more from the queue until block1 completes.
Block1, though, has placed block2 on the queue, and is waiting for it to complete. Block2 has indeed been placed on the queue, but it will never be executed. Block1 is "waiting" for block2 to complete, but block2 is sitting on a queue, and the code that pulls it off the queue and executes it will not run until block1 completes.
Deadlock from NOT reentering dispatch_sync
Now, what if we change the code to this...
block1 = {
dispatch_sync(queue, block2);
}
dispatch_async(queue, block1);
We are not technically reentering dispatch_sync. However, we still have the same scenario, it's just that the thread that kicked off block1 is not waiting for it to finish.
We are still running block1, waiting for block2 to finish, but the thread that will run block2 must finish with block1 first. This will never happen because the code to process block1 is waiting for block2 to be taken off the queue and executed.
Thus reentrancy for dispatch queues is not technically reentering the same function, but reentering the same queue processing.
Deadlocks from NOT reentering the queue at all
In it's most simple case (and most common), let's assume [self foo] gets called on the main thread, as is common for UI callbacks.
-(void) foo {
dispatch_sync(dispatch_get_main_queue(), ^{
// Never gets here
});
}
This doesn't "reenter" the dispatch queue API, but it has the same effect. We are running on the main thread. The main thread is where the blocks are taken off the main queue and processed. The main thread is currently executing foo and a block is placed on the main-queue, and foo then waits for that block to be executed. However, it can only be taken off the queue and executed after the main thread gets done with its current work.
This will never happen because the main thread will not progress until `foo completes, but it will never complete until that block it is waiting for runs... which will not happen.
In my understanding, you can have a deadlock using dispatch_sync only
if the thread you are running on is the same thread where the block is
dispatch into.
As the aforementioned example illustrates, that's not the case.
Furthermore, there are other scenarios that are similar, but not so obvious, especially when the sync access is hidden in layers of method calls.
Avoiding deadlocks
The only sure way to avoid deadlocks is to never call dispatch_sync (that's not exactly true, but it's close enough). This is especially true if you expose your queue to users.
If you use a self-contained queue, and control its use and target queues, you can maintain some control when using dispatch_sync.
There are, indeed, some valid uses of dispatch_sync on a serial queue, but most are probably unwise, and should only be done when you know for certain that you will not be 'sync' accessing the same or another resource (the latter is known as deadly embrace).
EDIT
Jody, Thanks a lot for your answer. I really understood all of your
stuff. I would like to put more points...but right now I cannot. 😢 Do
you have any good tips in order to learn this under the hood stuff? –
Lorenzo B.
Unfortunately, the only books on GCD that I've seen are not very advanced. They go over the easy surface level stuff on how to use it for simple general use cases (which I guess is what a mass market book is supposed to do).
However, GCD is open source. Here is the webpage for it, which includes links to their svn and git repositories. However, the webpage looks old (2010) and I'm not sure how recent the code is. The most recent commit to the git repository is dated Aug 9, 2012.
I'm sure there are more recent updates; but not sure where they would be.
In any event, I doubt the conceptual frameworks of the code has changed much over the years.
Also, the general idea of dispatch queues is not new, and has been around in many forms for a very long time.
Many moons ago, I spent my days (and nights) writing kernel code (worked on what we believe to have been the very first symmetric multiprocessing implementation of SVR4), and then when I finally breached the kernel, I spent most of my time writing SVR4 STREAMS drivers (wrapped by user space libraries). Eventually, I made it fully into user space, and built some of the very first HFT systems (though it wasn't called that back then).
The dispatch queue concept was prevalent in every bit of that. It's emergence as a generally available user space library is only a somewhat recent development.
Edit #2
Jody, thanks for your edit. So, to recap a serial dispatch queue is
not reentrant since it could produce an invalid state (a deadlock).
On the contrary, an reentrant function will not produce it. Am I right?
– Lorenzo B.
I guess you could say that, because it does not support reentrant calls.
However, I think I would prefer to say that the deadlock is the result of preventing invalid state. If anything else occurred, then either the state would be compromised, or the definition of the queue would be violated.
Core Data's performBlockAndWait
Consider -[NSManagedObjectContext performBlockAndWait]. It's non-asynchronous, and it is reentrant. It has some pixie dust sprinkled around the queue access so that the second block runs immediately, when called from "the queue." Thus, it has the traits I described above.
[moc performBlock:^{
[moc performBlockAndWait:^{
// This block runs immediately, and to completion before returning
// However, `dispatch_async`/`dispatch_sync` would deadlock
}];
}];
The above code does not "produce a deadlock" from reentrancy (but the API can't avoid deadlocks entirely).
However, depending on who you talk to, doing this can produce invalid (or unpredictable/unexpected) state. In this simple example, it's clear what's happening, but in more complicated parts it can be more insidious.
At the very least, you must be very careful about what you do inside a performBlockAndWait.
Now, in practice, this is only a real issue for main-queue MOCs, because the main run loop is running on the main queue, so performBlockAndWait recognizes that and immediately executes the block. However, most apps have a MOC attached to the main queue, and respond to user save events on the main queue.
If you want to watch how dispatch queues interact with the main run loop, you can install a CFRunLoopObserver on the main run loop, and watch how it processes the various input sources in the main run loop.
If you've never done that, it's an interesting and educational experiment (though you can't assume what you observe will always be that way).
Anyway, I generally try to avoid both dispatch_sync and performBlockAndWait.
I'm doing some customization in iOS, I'm subclassing a system class that executes a method asynchronously (presumably with dispatch_async)
Sample code:
-(void)originalAsyncMethod {
[super originalAsyncMethod];
dispatch_async(dispatch_get_main_queue(), ^{
//do something that needs to happen just after originalAsyncMethod finishes executing
});
}
Is there a way I can make sure my custom code runs AFTER the async super method is executed?
It's unclear to me wether this would be possible based on your question, but if you have direct access to the implementation of super, then this shouldn't be to hard to achieve.
First, assuming that you have access to the super class and that the super implementation also dispatches asynchronously to the main queue, then you don't actually have to do anything to get this working expectedly. When you use dispatch_get_main_queue() you're adding your dispatch block to the end of a serial queue on the main thread that is executed in FIFO (first in first out) order.
The second option is also pretty heavily reliant on having access to the super implementation, as it would require you manually create your own dispatch queue to execute tasks on. I think it goes without saying that if you use a serial dispatch queue then you have FIFO ordering in this queue same as you dispatch_get_main_queue(), only you wouldn't have to execute on the main thread.
And the last option I can think of wouldn't necessarily require you to modify the super class, but would require you to know the queue on which super was executing. (and still might not work right if it's a global queue) By using a dispatch_barrier, you could allow your super implementation to execute asynchronously on a concurrent queue knowing that the subclass dispatch block has also been added to the queue (via dispatch_barrier), and will be executed once the super dispatch (and any other previous submissions to the queue) has completed.
Quoting the docs
A dispatch barrier allows you to create a synchronization point within
a concurrent dispatch queue. When it encounters a barrier, a
concurrent queue delays the execution of the barrier block (or any
further blocks) until all blocks submitted before the barrier finish
executing. At that point, the barrier block executes by itself. Upon
completion, the queue resumes its normal execution behavior.
Following an earlier question on SO, I'm now looking to compare two different grand central dispatch queues to try and determine if the current code is being run on the main thread or not. My question simply: is this a valid way of achieving this? Or are there some pitfalls of doing this that I haven't considered?
if (dispatch_get_current_queue() != dispatch_get_main_queue()) {
// We are currently on a background queue
} else {
// We are on the main queue
}
Cheers
Comparing the current queue against the main queue is not a valid way to check whether you are running on the main thread.
Use [NSThread isMainThread] or pthread_main_np() to explicitly check whether you are on the main thread if that is what you want to know.
You can be on the main thread without the current queue being the main queue, and you can be on the main queue without the current thread being the main thread (the latter only if dispatch_main() has been called, but still).
In recent releases this is documented explicitly in the CAVEATS section of the dispatch_get_main_queue(3) manpage:
The result of dispatch_get_main_queue() may or may not equal the result of dispatch_get_current_queue() when called on the main thread. Comparing the two is not a valid way to test whether code is executing on the main thread. Foundation/AppKit programs should use [NSThread isMainThread]. POSIX programs may use pthread_main_np(3).
In general you should avoid using queue pointer comparison to influence program logic. Dispatch queues exist in a dependency tree (the target queue hierarchy) and comparing individual leaves in that tree without taking their interdependency into account does not provide sufficient information to make safe decisions.
If you really need program logic based on queue interdependency, use the dispatch_get_specific(3)/dispatch_queue_set_specific(3) APIs which are target-queue aware and much more explicit.
Imagine you want to do many thing in the background of an iOS application but you code it properly so that you create threads (for example using GCD) do execute this background activity.
Now what if you need at some point to write update a variable but this update can occur or any of the threads you created.
You obviously want to protect that variable and you can use the keyword #synchronized to create the locks for you but here is the catch (extract from the Apple documentation)
The #synchronized() directive locks a section of code for use by a
single thread. Other threads are blocked until the thread exits the
protected code—that is, when execution continues past the last
statement in the #synchronized() block.
So that means if you synchronized an object and two threads are writing it at the same time, even the main thread will block until both threads are done writing their data.
An example of code that will showcase all this:
// Create the background queue
dispatch_queue_t queue = dispatch_queue_create("synchronized_example", NULL);
// Start working in new thread
dispatch_async(queue, ^
{
// Synchronized that shared resource
#synchronized(sharedResource_)
{
// Write things on that resource
// If more that one thread access this piece of code:
// all threads (even main thread) will block until task is completed.
[self writeComplexDataOnLocalFile];
}
});
// won’t actually go away until queue is empty
dispatch_release(queue);
So the question is fairly simple: How to overcome this ? How can we securely add a locks on all the threads EXCEPT the main thread which, we know, doesn't need to be blocked in that case ?
EDIT FOR CLARIFICATION
As you some of you commented, it does seem logical (and this was clearly what I thought at first when using synchronized) that only two the threads that are trying to acquire the lock should block until they are both done.
However, tested in a real situation, this doesn't seem to be the case and the main thread seems to also suffer from the lock.
I use this mechanism to log things in separate threads so that the UI is not blocked. But when I do intense logging, the UI (main thread) is clearly highly impacted (scrolling is not as smooth).
So two options here: Either the background tasks are too heavy that even the main thread gets impacted (which I doubt), or the synchronized also blocks the main thread while performing the lock operations (which I'm starting reconsidering).
I'll dig a little further using the Time Profiler.
I believe you are misunderstanding the following sentence that you quote from the Apple documentation:
Other threads are blocked until the thread exits the protected code...
This does not mean that all threads are blocked, it just means all threads that are trying to synchronise on the same object (the _sharedResource in your example) are blocked.
The following quote is taken from Apple's Thread Programming Guide, which makes it clear that only threads that synchronise on the same object are blocked.
The object passed to the #synchronized directive is a unique identifier used to distinguish the protected block. If you execute the preceding method in two different threads, passing a different object for the anObj parameter on each thread, each would take its lock and continue processing without being blocked by the other. If you pass the same object in both cases, however, one of the threads would acquire the lock first and the other would block until the first thread completed the critical section.
Update: If your background threads are impacting the performance of your interface then you might want to consider putting some sleeps into the background threads. This should allow the main thread some time to update the UI.
I realise you are using GCD but, for example, NSThread has a couple of methods that will suspend the thread, e.g. -sleepForTimeInterval:. In GCD you can probably just call sleep().
Alternatively, you might also want to look at changing the thread priority to a lower priority. Again, NSThread has the setThreadPriority: for this purpose. In GCD, I believe you would just use a low priority queue for the dispatched blocks.
I'm not sure if I understood you correctly, #synchronize doesn't block all threads but only the ones that want to execute the code inside of the block. So the solution probably is; Don't execute the code on the main thread.
If you simply want to avoid having the main thread acquire the lock, you can do this (and wreck havoc):
dispatch_async(queue, ^
{
if(![NSThread isMainThread])
{
// Synchronized that shared resource
#synchronized(sharedResource_)
{
// Write things on that resource
// If more that one thread access this piece of code:
// all threads (even main thread) will block until task is completed.
[self writeComplexDataOnLocalFile];
}
}
else
[self writeComplexDataOnLocalFile];
});