How to use GCD for lightweight transactional locking of resources? - ios

I'm trying to use GCD as a replacement for dozens of atomic properties. I remember at WWDC they were talking about that GCD could be used for efficient transactional locking mechanisms.
In my OpenGL ES runloop method I put all drawing code in a block executed by dispatch_sync on a custom created serial queue. The runloop is called by a CADisplayLink which is to my knowledge happening on the main thread.
There are ivars and properties which are used both for drawing but also for controlling what will be drawn. The problem is that there must be some locking in place to prevent concurrency problems, and a way of transactionally querying and modifying the state of the OpenGL ES scene from the main thread between two drawn frames.
I can modify a group of properties in a transactional way with GCD by executing a block on that serial queue.
But it seems I can't read values into the main thread, using GCD, while blocking the queue that executes the drawing code. dispatch_synch doesn't have a return value, but I want to get access to presentation values exactly between the drawing of two frames both for reading and writing.
Is it this barrier thing they were talking about? How does that work?

This is what the async writer / sync reader model was designed to accomplish. Let's say you have an ivar (and for purpose of discussion let's assume that you've gone a wee bit further and encapsulated all your ivars into a single structure, just for simplicity's sake:
struct {
int x, y;
char *n;
dispatch_queue_t _internalQueue;
} myIvars;
Let's further assume (for brevity) that you've initialized the ivars in a dispatch_once() and created the _internalQueue as a serial queue with dispatch_queue_create() earlier in the code.
Now, to write a value:
dispatch_async(myIvars._internalQueue, ^{ myIvars.x = 10; });
dispatch_async(myIvars._internalQueue, ^{ myIvars.n = "Hi there"; });
And to read one:
__block int val; __block char *v;
dispatch_sync(myIvars._internalQueue, ^{ val = myIvars.x; });
dispatch_sync(myIvars._internalQueue, ^{ v = myIvars.n; })
Using the internal queue makes sure everything is appropriately serialized and that writes can happen asynchronously but reads wait for all pending writes to complete before giving you back the value. A lot of "GCD aware" data structures (or routines that have internal data structures) incorporate serial queues as implementation details for just this purpose.

dispatch_sync allows you to specify a second argument as completion block where you can get the values from your serial queue and use them on your main thread.So it would look something like
dispatch_sync(serialQueue,^{
//execute a block
dispatch_async(get_dispatch_main_queue,^{
//use your calculations here
});
});
And serial queues handle the concurrency part themselves. So if another piece is trying to access the same code at the same time it will be handled by the queue itself.Hope this was of little help.

Related

synchronized block within dispatch_async

I have seen code that dispatch async to a main queue or private dispatch queue (serial) and then in the dispatch code block is #synchronized. Under what circumstance do you want to do that? Isn't a serial queue already providing the synchronization needed?
Can the synchronized block be replaced with another GCD dispatch?
#synchronized() ensures that contained code (for a given token as the argument to #synchronized) is only run on one thread at a time.
Blocks submitted to a serial queue are executed one at a time, ie. a given block is not executed until all blocks submitted before it have finished executing. As long as a shared resource is only being accessed from code running on a serial queue, there's no need to synchronize/lock access to it. However, just because a given queue is serial, doesn't mean that other queues/threads (even serial queues!) aren't running simultaneously, and accessing the same shared resource.
Using #synchronized() is one way to prevent these multiple threads/queues from accessing the resource at the same time. Note that all code that access the shared resource needs to be wrapped with #synchronized().
Yes, you can use another GCD dispatch instead of a synchronized block. The "GCD way" of doing this would be to serialize all access to the shared resource using a serial queue. So, any time access to the shared resource needs to be made, you dispatch that code (using either dispatch_sync() or dispatch_async() depending on use case) to the serial queue associated with the resource. Of course, this means that the resource-access-queue must be visible/accessible to all parts of the program that access the shared resource. (You essentially have the same problem with #synchronized() in that its lock token must be accessible wherever it needs to be used, but it's a little easier since it can just be a string constant.)
The queue yes, it is synchronized, but if you access any 'external' object within, they are not synchronized.
If there are many threads, you know that each one will have its turn on the object. A tipical case is when you perform a CoreData import asynchronously, you have to #synchronized the context or the store coordinator.
Thread-safe is about making mutable shared state either immutable, or unshared. In this case, synchronize and serial queues are ways to temporarily unshare (prevent concurrent access), and both are valid.
However, consider the case where you have disjoint sets of related info inside the same object. For example, a bill with 1) parts of an address (city, street, etc), and 2) price, taxes, discount. Both need to be protected to avoid inconsistent state (object A sets a new street, while object B reads the old city and the new street), but both are unrelated. In this case, you should use the lower level of granularity to avoid blocks between unrelated code.
So a rule would be: don't use synchronize on unrelated sets of variables inside the same object, because it would cause unneeded blocks between them. You can use a queue + synchronize, or a queue per set, but not synchronized on both sets.
The key is that synchronize refers to the one and only intrinsic lock of the object, and that token can only be held by once. It accomplishes the same goal as if you routed all related code through one queue, except that you can have multiple queues (and thus, lower granularity), but only one intrinsic lock.
Back to your example. Assuming that the object is documented as “state X is protected by synchronize”, the use of synchronize inside the queue is useful to block related methods that could access that same state. So maybe queue and synchronized are protecting different things, or the serial queue is there to perform a different task.
Another reason to prefer a queue is to write a more sophisticated pattern like a read-write lock. Example:
NSMutableDictionary *_dic = [NSMutableDictionary new];
dispatch_queue_t _queue = dispatch_queue_create("com.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);
- (id) objectForKey:(id)key
{
__block obj;
dispatch_sync(_queue, ^{
obj = [_dic objectForKey: key];
});
return obj;
}
- (void) setObject:(id)obj forKey:(id)key
{
// exclusive access while writing
dispatch_barrier_async(_queue, ^{
[_dic setObject:obj forKey:key];
});
}

synchronized blocks and dispatch_async

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.

What behavior is guaranteed with Grand Central Dispatch in Objective-C?

I think the best way to ask this question is with some code:
//Main method
for(int i = 0; i < 10; i++)
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self foo:i];
});
}
- (void) foo: (int) i
{
#synchronized(self)
{
NSLog(#"%d",i);
}
}
In this case, is it guaranteed that the numbers 0-9 will be printed out in order? Is there ever a chance that one of the threads that is waiting on the run queue, will be skipped over? How about in reality. Realistically, does that ever happen? What if I wanted the behavior above (still using threads); how could I accomplish this?
In this case, is it guaranteed that the numbers 0-9 will be printed
out in order?
No.
Is there ever a chance that one of the threads that is waiting on the
run queue, will be skipped over?
Unclear what "skipped over" means. If it means "will the blocks be executed in order?" the answer is "probably, but it is an implementation detail".
How about in reality. Realistically, does that ever happen?
Irrelevant. If you you are writing concurrency code based on assumptions about realistic implementation details, you are writing incorrect concurrency code.
What if I wanted the behavior above (still using threads); how could I
accomplish this?
Create a serial dispatch queue and dispatch to that queue in the order you need things to be executed. Note that this is significantly faster than #synchronized() (of course, #synchronized() wouldn't work for you anyway in that it doesn't guarantee order, but merely exclusivity).
From the documentation of dispatch_get_global_queue
Blocks submitted to these global concurrent queues may be executed concurrently with respect to each other.
So that means there is no guaranteed of anything there. You are passing a block of code to the queue and the queue takes it from there.

#synchronized block versus GCD dispatch_async()

Essentially, I have a set of data in an NSDictionary, but for convenience I'm setting up some NSArrays with the data sorted and filtered in a few different ways. The data will be coming in via different threads (blocks), and I want to make sure there is only one block at a time modifying my data store.
I went through the trouble of setting up a dispatch queue this afternoon, and then randomly stumbled onto a post about #synchronized that made it seem like pretty much exactly what I want to be doing.
So what I have right now is...
// a property on my object
#property (assign) dispatch_queue_t matchSortingQueue;
// in my object init
_sortingQueue = dispatch_queue_create("com.asdf.matchSortingQueue", NULL);
// then later...
- (void)sortArrayIntoLocalStore:(NSArray*)matches
{
dispatch_async(_sortingQueue, ^{
// do stuff...
});
}
And my question is, could I just replace all of this with the following?
- (void)sortArrayIntoLocalStore:(NSArray*)matches
{
#synchronized (self) {
// do stuff...
};
}
...And what's the difference between the two anyway? What should I be considering?
Although the functional difference might not matter much to you, it's what you'd expect: if you #synchronize then the thread you're on is blocked until it can get exclusive execution. If you dispatch to a serial dispatch queue asynchronously then the calling thread can get on with other things and whatever it is you're actually doing will always occur on the same, known queue.
So they're equivalent for ensuring that a third resource is used from only one queue at a time.
Dispatching could be a better idea if, say, you had a resource that is accessed by the user interface from the main queue and you wanted to mutate it. Then your user interface code doesn't need explicitly to #synchronize, hiding the complexity of your threading scheme within the object quite naturally. Dispatching will also be a better idea if you've got a central actor that can trigger several of these changes on other different actors; that'll allow them to operate concurrently.
Synchronising is more compact and a lot easier to step debug. If what you're doing tends to be two or three lines and you'd need to dispatch it synchronously anyway then it feels like going to the effort of creating a queue isn't worth it — especially when you consider the implicit costs of creating a block and moving it over onto the heap.
In the second case you would block the calling thread until "do stuff" was done. Using queues and dispatch_async you will not block the calling thread. This would be particularly important if you call sortArrayIntoLocalStore from the UI thread.

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