synchronized blocks and dispatch_async - ios

What happens to the lock in IOS using #synchronized() when we call dispatch_async() within the block.
For ex:
id myID
-(void) foobar
{
#synchronized(myID){
dispatch_async(){ //do stuff with myID};
}
}
Is the lock still valid within the dispatch_async call? Or more importantly is there any drawbacks in using another #synchronized() call inside dispatch_async()?

Assuming you're trying to synchronize the interaction with this myID object in the background queue, you want it the other way around, the lock inside the dispatched block. Right now you have:
#synchronized(myID) {
dispatch_async(queue, ^{
// do stuff with myID
});
}
That's synchronizing the process of adding the dispatched block to your queue, but does not synchronize what you're doing in the background. I suspect that's not what you meant.
You probably intended:
dispatch_async(queue, ^{
#synchronized(myID) {
// do stuff with myID
}
});
It looks very similar, but results in an entirely different behavior. Now, the work dispatched to the background queue is being synchronized.
As a further refinement, if this dispatched block is possibly slow (and I assume it may be), then you'd probably want to constrain the #synchronized block as much as possible:
dispatch_async(queue, ^{
// do slow stuff in preparation for interacting with `myID`
#synchronized(myID) {
// quickly do stuff with myID
}
// do anything else here
});
If you do all of the background block within a #synchronized block, you may defeat the entire purpose for dispatching it to the background, namely to minimize impact on the main queue. This last rendition mitigates that problem.
As a final observation, if you have a serial queue (or a non-global concurrent queue in which you do updates with a barrier), that's often used as a technique that eliminates the need for locks altogether, as long as all updates and inquiries for myID are dispatched to that queue. See Eliminating Lock-Based Code in the Concurrency Programming Guide.

The lock there would just prevent two different blocks being dispatched at once. However they're dispatched asynchronously, so they may be performed then or may be performed arbitrarily far in the future. The dispatch call also won't wait for them to complete.
So the stuff inside the block isn't synchronised. Options to achieve that with minimal changes are a synchronous dispatch or just #synchronizing within the block.
Depending on what you're doing, the best idea might be to establish a serial dispatch queue and dispatch your blocks onto that.

Related

Cancel dispatch_async queue that is waiting for execution

Firstly, I know that there is a lot of question already for canceling dispatch_async, and I know it was said that running queue can't be stopped.
But there is nothing posted about waiting blocks in queue.
Let say I implement something like this.
#property(nonatomic)dispatch_queue_t My_queue = dispatch_queue_create("something", NULL)
dispatch_async(self.My_queue, ^(void){
// LONG TASK ONE
});
dispatch_async(self.My_queue, ^(void){
// LONG TASK TWO
});
dispatch_async(self.My_queue, ^(void){
// LONG TASK THREE
});
In new thread there will be queue with three block, one will be running, two will be waiting. How to cancel those two that are waiting?
The thing you're looking for is dispatch_suspend():
By suspending a dispatch object, your application can temporarily prevent the execution of any blocks associated with that object. The suspension occurs after completion of any blocks running at the time of the call.
dispatch_suspend(self.My_queue);
This means that the queue will execute the pending block (the first one), and will not execute the next ones. Once you get rid of the reference to the queue, it's retain count will decrease, and once it gets to zero the queue will be destroyed, along with the scheduled blocks (unless the blocks are referenced in other parts of the code).
As a side note, regarding coding style, property names should begin with a lowercase letter, and are recommended to follow the camelCase convention: myQueue instead of My_queue.

Clarifications on dispatch_queue, reentrancy and deadlocks

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.

If I have a dispatch_sync call followed by a second dispatch call within a dispatch_async block, does it matter if that second call is sync or async?

This code is for a pretty standard situation where I'm having my data model do some potentially slow data retrieval and I want to update my view with the data once the data retrieval finishes.
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
dispatch_sync(queue, ^{
//get a bunch of data
}
dispatch_sync(dispatch_get_main_queue(), ^{
[viewcontroller tellViewToReloadData];
}
}
My question here is, does it make much of a difference if that second dispatch_sync was instead dispatch_async?
This is my understanding of what's happening with the code above (I'm also using this as an opportunity to gauge my understanding of this overall topic):
The outer dispatch_async immediately returns, and the block with the two dispatch calls gets put into the concurrent queue.
At some point the outer async block will execute on some random thread, at which point the first dispatch_sync gets called and its block gets put into the concurrent queue.
At some point the inner first sync block will execute all of my data gathering operations on some random thread, and only when they are all finished will the first dispatch_sync return and the second dispatch_sync get called.
The second dispatch_sync gets called, and the block with the code to update the view gets put into the main queue. At some point this block executes and the view gets updated, and the second dispatch_sync returns.
The outer block is now finished executing so the concurrent queue is free to possibly push a task to the thread that was executing the outer block.
Now, my understanding is that if that second dispatch_sync was instead a dispatch_async call, the only change is that the thread executing the outer block is occupied for a slightly shorter amount of time because it doesn't have to wait for the block in the main queue to finish executing. Is this correct? By this reasoning it seems slightly better for the second dispatch_sync to be dispatch_async just because some thread will be occupied for a (probably trivially) shorter amount of time, but effectively it doesn't really matter.
First sync is pointless – you're already on queue.
dispatch_async(queue, ^{
//get a bunch of data
dispatch_sync(dispatch_get_main_queue(), ^{
[viewcontroller tellViewToReloadData];
});
});
sync is just async with waiting primitive, as if it was:
dispatch_semaphore_t s = dispatch_semaphore_create(0);
dispatch_async(queue, ^{
[viewcontroller tellViewToReloadData];
dispatch_semaphore_signal(s);
});
dispatch_semaphore_wait(s, DISPATCH_TIME_FOREVER);
Now, my understanding is that if that second dispatch_sync was instead a dispatch_async call, the only change is that the thread executing the outer block is occupied for a slightly shorter amount of time because it doesn't have to wait for the block in the main queue to finish executing. Is this correct?
Yes.
By this reasoning it seems slightly better for the second dispatch_sync to be dispatch_async just because some thread will be occupied for a (probably trivially) shorter amount of time, but effectively it doesn't really matter.
It may matter in situations when many threads are stuck waiting for main thread to complete being under stress loads. Then system would need to spawn more pthreads to continue to perform other concurrent tasks effectively and close excessive threads afterwards. Can't tell if that applies, but obviously there is no need to wait if you don't need results.

Calling code sequentially after dispatch_async

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.

is there a way that the synchronized keyword doesn't block the main thread

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];
});

Resources