The Guinea Pig in the Cocoa Mine

Underground Cocoa Experiments

Avoiding Deadlocks With Dispatch Queues

I have started to use dispatch queues more seriously in the past couple of years. To make the best use of dispatch queues, one has to really think differently about the design of classes, the design of their APIs and the overall architecture of the program. It took a while for me to grasp, but it is liberating at many different levels. I now feel I write better code, and feel more confident writing multi-threaded code (best of all, without having to actually manage threads).

I still find myself thinking about dispatch queues through other concepts I am more familiar with, though. For instance, serial queues are great for serializing access to a group of connected resources, like a CoreData stack. In this case, I still tend to think of a serial dispatch queue as a run loop in which you can just push any work that need to be done on those resources. Of course, dispatch queues would probably be insulted to be called run loops, and rightly so. For starters, they are very efficient and much easier to use.

That said, there is still one thing that run loops make easier to manage: timers. But with the help of PARDispatchQueue, I now have the best of both worlds.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@interface PARDispatchQueue : NSObject

// Creating Queues
+ (PARDispatchQueue *)globalDispatchQueue;
+ (PARDispatchQueue *)mainDispatchQueue;
+ (PARDispatchQueue *)dispatchQueueWithLabel:(NSString *)label;


// Dispatching Blocks
- (void)dispatchSynchronously:(PARDispatchBlock)block;
- (void)dispatchAsynchronously:(PARDispatchBlock)block;

// Managing Timers
- (BOOL)scheduleTimerWithName:(NSString *)name timeInterval:(NSTimeInterval)delay behavior:(FDTimerBehavior)behavior block:(FDDispatchBlock)block;
- (void)cancelTimerWithName:(NSString *)name;
- (void)cancelAllTimers;

// Deadlock Behaviors
@property (readonly) FDDeadlockBehavior deadlockBehavior;

typedef NS_ENUM(NSInteger, FDDeadlockBehavior)
{
    FDDeadlockBehaviorExecute,
    FDDeadlockBehaviorSkip,
    FDDeadlockBehaviorLog,
    FDDeadlockBehaviorAssert,
    FDDeadlockBehaviorBlock
};

// FDDeadlockBehaviorExecute: do not add the block to the queue, execute inline (default)
// FDDeadlockBehaviorSkip:    do not add the block to the queue, drop it silently
// FDDeadlockBehaviorLog:     do not add the block to the queue, log to console
// FDDeadlockBehaviorAssert:  do not add the block to the queue, raise an exception
// FDDeadlockBehaviorBlock:   add the block to the queue, and be damned



@end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@interface PARStore : NSObject

// Accessing Store Content
- (id)propertyListValueForKey:(NSString *)key;
- (void)setPropertyListValue:(id)plist forKey:(NSString *)key;
- (NSDictionary *)allRelevantValues;
- (void)setEntriesFromDictionary:(NSDictionary *)dictionary;
- (void)runTransaction:(FDDispatchBlock)block;

@end

// CoreData APIs
- (void)performBlock:(void (^)())block
- (void)performBlockAndWait:(void (^)())block;

// ...
[context performBlockAndWait:^{
    [context performBlockAndWait:^{
        [context save:NULL];
    }];
}];
1
2
3
4
for (int i = 0; i < 10; i++)
{
    printf("%i", i);
}

Comments