I have some model classes that I need to synchronize. There is a main object of type Library
that contains several Album
objects (imagine a music library, for example). Both the library and albums support serialization by implementing the NSCoding
protocol. I need to synchronize modifications to the library with album modifications and the serialization of both classes, so that I know the updates don’t step on each other’s toes and that I don’t serialize objects in the middle of an update.
I thought I would just pass all the objects a shared dispatch queue, dispatch_async
all the setter code and dispatch_sync
the getters. This is simple and easy, but it does not work, as the program flow is recursive:
// In the Library class
- (void) encodeWithCoder: (NSCoder*) encoder
{
dispatch_sync(queue, ^{
[encoder encodeObject:albums forKey:…];
});
}
// In the Album class, same queue as above
- (void) encodeWithCoder: (NSCoder*) encoder
{
dispatch_sync(queue, ^{
[encoder encodeObject:items forKey:…];
});
}
Now serializing the library triggers the album serialization and since both method use dispatch_sync
on the same queue, the code deadlocks. I have seen this pattern somewhere:
- (void) runOnSynchronizationQueue: (dispatch_block_t) block
{
if (dispatch_get_current_queue() == queue) {
block();
} else {
dispatch_sync(queue, block);
}
}
Does it make sense, will it work, is it safe? Is there an easier way to do the synchronization?
For an exposition on recursive locks in GCD, see the Recursive Locks section of the dispatch_async man page. To briefly summarize it, it's generally a good idea to rethink your object hierarchies when something like this occurs.
You can also use dispatch_set_target_queue()
to control the hierarchy of execution (targeting subordinate queues at higher level queues) once you've refactored your code such that it's the operations rather than the objects that need to be controlled, but that's also assuming that you weren't able to simply use completion callbacks to accomplish the same synchronization effect (which, frankly, is more recommended since abstract queue hierarchies can be difficult to conceptualize and debug).
I know that's not really the answer you were looking for, but you're kind of in a "you can't get there from here" situation with GCD and a more fundamental rethink of how to do things "the GCD way" is almost certainly necessary in this case.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With