How to implement retentate blocking mechanism in objective-c via GCD?

I have an objective-c class with some methods that use the GCD queue to provide concurrent access to the resource in series (the standard way to do this).

Some of these methods require calling other methods of the same class. Therefore, the locking mechanism must be re-enabled. Is there a standard way to do this?

I used each of these methods first

dispatch_sync(my_queue, ^{ // Critical section }); 

to synchronize access. As you know, when one of these methods calls another such method, a deadlock occurs because the dispatch_sync call stops the current executable file until another block is executed, which also cannot be executed, because execution in the queue has stopped. To solve this problem, I then used, for example, this method:

 - (void) executeOnQueueSync:(dispatch_queue_t)queue : (void (^)(void))theBlock { if (dispatch_get_current_queue() == queue) { theBlock(); } else { dispatch_sync(queue, theBlock); } } 

In each of my methods I use

 [self executeOnQueueSync:my_queue : ^{ // Critical section }]; 

I do not like this solution, because for each block with a different return type, I need to write a different method. Moreover, this problem looks very often to me, and I think that for this there should be a more acceptable, standard solution.

+6
objective-c locking grand-central-dispatch reentrantlock
Oct. 21 '13 at 12:12
source share
1 answer

First of all: dispatch_get_current_queue() deprecated. The canonical approach will now use dispatch_queue_set_specific . One such example might look like this:

 typedef dispatch_queue_t dispatch_recursive_queue_t; static const void * const RecursiveKey = (const void*)&RecursiveKey; dispatch_recursive_queue_t dispatch_queue_create_recursive_serial(const char * name) { dispatch_queue_t queue = dispatch_queue_create(name, DISPATCH_QUEUE_SERIAL); dispatch_queue_set_specific(queue, RecursiveKey, (__bridge void *)(queue), NULL); return queue; } void dispatch_sync_recursive(dispatch_recursive_queue_t queue, dispatch_block_t block) { if (dispatch_get_specific(RecursiveKey) == (__bridge void *)(queue)) block(); else dispatch_sync(queue, block); } 

This pattern is quite applicable, but it may not be bulletproof, because you can create nested recursive queues using dispatch_set_target_queue , and trying to set the queue to the outer queue from the inside will be deadlocked, although you are already “inside the lock” (in ridiculous quotes, because it only looks like a lock, it’s actually something else: the queue means a question, right?) for the external. (You can get around this by wrapping the calls on dispatch_set_target_queue and preserving your own out-of-band targeting schedule, etc., but that left it as an exercise for the reader.)

You keep saying:

I do not like this solution, because for each block with different return types, I need to write a different method.

The general idea behind this “save state after queue” pattern is that you protect a private state; why would you "bring your turn"? If this applies to several objects sharing state protection, then give them an integral way to find the queue (i.e. either insert it during initialization, or place it somewhere that will be accessible to all interested parties). It is unclear how “bring turn” will be useful here.

+10
Oct 21 '13 at 13:12
source share



All Articles