Skip to content

Instantly share code, notes, and snippets.

@sumerman
Created September 4, 2011 10:00
Show Gist options
  • Save sumerman/1192607 to your computer and use it in GitHub Desktop.
Save sumerman/1192607 to your computer and use it in GitHub Desktop.
Intent of this code is to guide protected concurrent access to collection of states.
#import <Foundation/Foundation.h>
typedef id (^MVGetterBlock)(void);
typedef void (^MVSetterBlock)(id);
typedef id (^MVStateOpBlock)(id);
typedef NSOperation * (^MVOpSetupBlock)(MVGetterBlock, MVSetterBlock);
@interface MVStateStorage : NSObject {
__block NSMutableDictionary *data;
__block NSMutableDictionary *queues;
}
- (void)scheduleBlock:(MVStateOpBlock)block forKey:(id)key;
- (void)setupOperationForKey:(id)key withBlock:(MVOpSetupBlock)block;
- (void)cancelOperationsForKey:(id)key;
- (NSDictionary *)dictionaryRepresentation;
- (NSArray *)allKeys;
@end
#import "MVStateStorage.h"
@implementation MVStateStorage
- (id)init {
self = [super init];
if (self) {
data = [[NSMutableDictionary alloc] init];
queues = [[NSMutableDictionary alloc] init];
}
return self;
}
- (void)dealloc {
[queues enumerateKeysAndObjectsUsingBlock:^(id key, NSOperationQueue *obj, BOOL *s) {
[obj cancelAllOperations];
[obj waitUntilAllOperationsAreFinished];
[obj setSuspended:YES];
}];
[queues release];
[data release];
[super dealloc];
}
- (NSOperationQueue *)queueForKey:(id)key {
NSOperationQueue *q = [queues objectForKey:key];
if (!q) {
@synchronized(queues) {
q = [[NSOperationQueue alloc] init];
[q setMaxConcurrentOperationCount:1];
[queues setObject:q forKey:key];
[q release];
}
}
return q;
}
- (void)cancelOperationsForKey:(id)key {
[[self queueForKey:key] cancelAllOperations];
}
- (void)scheduleBlock:(MVStateOpBlock)block forKey:(id)key {
NSOperationQueue *q = [self queueForKey:key];
[q addOperationWithBlock:^{
id val = nil;
id res = nil;
@synchronized(data) {
val = [data objectForKey:key];
}
NSAutoreleasePool *p = [NSAutoreleasePool new];
res = block(val);
if (val != res) {
if (!res) {
[[NSOperationQueue currentQueue] cancelAllOperations];
@synchronized(data) {
[data removeObjectForKey:key];
}
return;
}
else {
@synchronized(data) {
[data setObject:res forKey:key];
}
}
}
[p release];
}];
}
#define PREVENT \
if (preventAccess) {\
[NSException raise:NSInternalInconsistencyException format:@"Setter function called in operation *setup* block."];\
}
- (void)setupOperationForKey:(id)key withBlock:(MVOpSetupBlock)block {
NSOperationQueue *q = [self queueForKey:key];
__block BOOL preventAccess = YES;
__block id cachedVal = nil;
MVGetterBlock getter = ^{
PREVENT
if (cachedVal) return cachedVal;
@synchronized(data) {
return [data objectForKey:key];
}
};
MVSetterBlock setter = ^(id newVal) {
PREVENT
@synchronized(data) {
cachedVal = newVal;
[data setObject:newVal forKey:key];
}
};
NSAutoreleasePool *p = [NSAutoreleasePool new];
NSOperation *op = block(getter, setter);
preventAccess = NO;
cachedVal = nil;
[q addOperation:op];
[p release];
}
#undef PREVENT
- (NSDictionary *)dictionaryRepresentation {
@synchronized(data) {
return [[[NSDictionary alloc] initWithDictionary:data copyItems:YES] autorelease];
}
}
- (NSArray *)allKeys {
@synchronized(data) {
return [[[data allKeys] copy] autorelease];
}
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment