Skip to content

Commit

Permalink
Support operation coallescing
Browse files Browse the repository at this point in the history
  • Loading branch information
nguyenhuy committed Nov 11, 2016
1 parent 910dd88 commit bceac42
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 35 deletions.
87 changes: 67 additions & 20 deletions PINCache/PINDiskCache.m
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,25 @@
static NSString * const PINDiskCachePrefix = @"com.pinterest.PINDiskCache";
static NSString * const PINDiskCacheSharedName = @"PINDiskCacheShared";

static NSString * const PINDiskCacheOperationIdentifierTrimToDate = @"PINDiskCacheOperationIdentifierTrimToDate";
static NSString * const PINDiskCacheOperationIdentifierTrimToSize = @"PINDiskCacheOperationIdentifierTrimToSize";
static NSString * const PINDiskCacheOperationIdentifierTrimToSizeByDate = @"PINDiskCacheOperationIdentifierTrimToSizeByDate";

typedef NS_ENUM(NSUInteger, PINDiskCacheCondition) {
PINDiskCacheConditionNotReady = 0,
PINDiskCacheConditionReady = 1,
};

static PINOperationDataCoallescingBlock PINTrimmingSizeCoallescingBlock = ^id(id existingSize, id newSize) {
NSComparisonResult result = [((NSNumber *)existingSize) compare:(NSNumber *)newSize];
return (result == NSOrderedDescending) ? newSize : existingSize;
};

static PINOperationDataCoallescingBlock PINTrimmingDateCoallescingBlock = ^id(id existingDate, id newDate) {
NSComparisonResult result = [((NSDate *) existingDate) compare:newDate];
return (result == NSOrderedDescending) ? newDate : existingDate;
};

@interface PINDiskCache () {
NSConditionLock *_instanceLock;

Expand Down Expand Up @@ -632,43 +646,76 @@ - (void)removeObjectForKey:(NSString *)key block:(PINDiskCacheObjectBlock)block
- (void)trimToSize:(NSUInteger)trimByteCount block:(PINDiskCacheBlock)block
{
__weak PINDiskCache *weakSelf = self;
[self.operationQueue addOperation:^{

PINOperationBlock operation = ^(id data) {
PINDiskCache *strongSelf = weakSelf;
[strongSelf trimToSize:trimByteCount];

if (block) {
[strongSelf trimToSize:((NSNumber *)data).unsignedIntegerValue];
};

dispatch_block_t completion = nil;
if (block) {
completion = ^{
PINDiskCache *strongSelf = weakSelf;
block(strongSelf);
}
} withPriority:PINOperationQueuePriorityLow];
};
}

[self.operationQueue addOperation:operation
withPriority:PINOperationQueuePriorityLow
identifier:PINDiskCacheOperationIdentifierTrimToSize
data:[NSNumber numberWithUnsignedInteger:trimByteCount]
dataCoallescingBlock:PINTrimmingSizeCoallescingBlock
completion:completion];
}

- (void)trimToDate:(NSDate *)trimDate block:(PINDiskCacheBlock)block
{
__weak PINDiskCache *weakSelf = self;
[self.operationQueue addOperation:^{

PINOperationBlock operation = ^(id data){
PINDiskCache *strongSelf = weakSelf;
[strongSelf trimToDate:trimDate];

if (block) {
[strongSelf trimToDate:(NSDate *)data];
};

dispatch_block_t completion = nil;
if (block) {
completion = ^{
PINDiskCache *strongSelf = weakSelf;
block(strongSelf);
}
} withPriority:PINOperationQueuePriorityLow];
};
}

[self.operationQueue addOperation:operation
withPriority:PINOperationQueuePriorityLow
identifier:PINDiskCacheOperationIdentifierTrimToDate
data:trimDate
dataCoallescingBlock:PINTrimmingDateCoallescingBlock
completion:completion];
}

- (void)trimToSizeByDate:(NSUInteger)trimByteCount block:(PINDiskCacheBlock)block
{
__weak PINDiskCache *weakSelf = self;

[self.operationQueue addOperation:^{
PINOperationBlock operation = ^(id data){
PINDiskCache *strongSelf = weakSelf;
[strongSelf trimToSizeByDate:trimByteCount];

if (block) {
[strongSelf trimToSizeByDate:((NSNumber *)data).unsignedIntegerValue];
};

dispatch_block_t completion = nil;
if (block) {
completion = ^{
PINDiskCache *strongSelf = weakSelf;
block(strongSelf);
}
} withPriority:PINOperationQueuePriorityLow];
};
}

[self.operationQueue addOperation:operation
withPriority:PINOperationQueuePriorityLow
identifier:PINDiskCacheOperationIdentifierTrimToSizeByDate
data:[NSNumber numberWithUnsignedInteger:trimByteCount]
dataCoallescingBlock:PINTrimmingSizeCoallescingBlock
completion:completion];
}

- (void)removeAllObjects:(PINDiskCacheBlock)block
Expand Down
4 changes: 4 additions & 0 deletions PINCache/PINOperationQueue.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ typedef NS_ENUM(NSUInteger, PINOperationQueuePriority) {
PINOperationQueuePriorityHigh,
};

typedef void(^PINOperationBlock)(id _Nullable data);
typedef _Nullable id(^PINOperationDataCoallescingBlock)(id _Nullable existingData, id _Nullable newData);

@protocol PINOperationReference;

@interface PINOperationQueue : NSObject
Expand All @@ -27,6 +30,7 @@ typedef NS_ENUM(NSUInteger, PINOperationQueuePriority) {

- (id <PINOperationReference>)addOperation:(dispatch_block_t)operation;
- (id <PINOperationReference>)addOperation:(dispatch_block_t)operation withPriority:(PINOperationQueuePriority)priority;
- (id <PINOperationReference>)addOperation:(PINOperationBlock)operation withPriority:(PINOperationQueuePriority)priority identifier:(NSString *)identifier data:(nullable id)data dataCoallescingBlock:(nullable PINOperationDataCoallescingBlock)dataCoallescingBlock completion:(nullable dispatch_block_t)completion;

/**
* Marks the operation as cancelled
Expand Down
96 changes: 81 additions & 15 deletions PINCache/PINOperationQueue.m
Original file line number Diff line number Diff line change
Expand Up @@ -35,28 +35,35 @@ @interface PINOperationQueue () {
NSMutableOrderedSet<PINOperation *> *_highPriorityOperations;

NSMapTable<id<PINOperationReference>, PINOperation *> *_referenceToOperations;
NSMapTable<NSString *, PINOperation *> *_identifierToOperations;
}

@end

@interface PINOperation : NSObject

@property (nonatomic, strong) dispatch_block_t block;
@property (nonatomic, strong) PINOperationBlock block;
@property (nonatomic, strong) id <PINOperationReference> reference;
@property (nonatomic, assign) PINOperationQueuePriority priority;
@property (nonatomic, strong) dispatch_block_t completion;
@property (nonatomic, strong) NSString *identifier;
@property (nonatomic, strong) id data;

+ (instancetype)operationWithBlock:(dispatch_block_t)block reference:(id <PINOperationReference>)reference priority:(PINOperationQueuePriority)priority;
+ (instancetype)operationWithBlock:(PINOperationBlock)block reference:(id <PINOperationReference>)reference priority:(PINOperationQueuePriority)priority identifier:(nullable NSString *)identifier data:(nullable id)data completion:(nullable dispatch_block_t)completion;

@end

@implementation PINOperation

+ (instancetype)operationWithBlock:(dispatch_block_t)block reference:(id<PINOperationReference>)reference priority:(PINOperationQueuePriority)priority
+ (instancetype)operationWithBlock:(PINOperationBlock)block reference:(id<PINOperationReference>)reference priority:(PINOperationQueuePriority)priority identifier:(NSString *)identifier data:(id)data completion:(dispatch_block_t)completion
{
PINOperation *operation = [[self alloc] init];
operation.block = block;
operation.reference = reference;
operation.priority = priority;
operation.identifier = identifier;
operation.data = data;
operation.completion = completion;

return operation;
}
Expand Down Expand Up @@ -98,6 +105,7 @@ - (instancetype)initWithMaxConcurrentOperations:(NSUInteger)maxConcurrentOperati
_highPriorityOperations = [[NSMutableOrderedSet alloc] init];

_referenceToOperations = [NSMapTable weakToWeakObjectsMapTable];
_identifierToOperations = [NSMapTable weakToWeakObjectsMapTable];
}
return self;
}
Expand Down Expand Up @@ -127,27 +135,79 @@ + (instancetype)sharedOperationQueue

- (id <PINOperationReference>)addOperation:(dispatch_block_t)block
{
return [self addOperation:block withPriority:PINOperationQueuePriorityDefault];
return [self addOperation:block withPriority:PINOperationQueuePriorityDefault];
}

- (id <PINOperationReference>)addOperation:(dispatch_block_t)block withPriority:(PINOperationQueuePriority)priority
{
id <PINOperationReference> reference = [self nextOperationReference];
PINOperation *operation = [PINOperation operationWithBlock:^(id data) { block(); }
reference:[self nextOperationReference]
priority:priority
identifier:nil
data:nil
completion:nil];
[self lock];
[self locked_addOperation:operation];
[self unlock];

NSMutableOrderedSet *queue = [self operationQueueWithPriority:priority];
[self scheduleNextOperations:NO];

PINOperation *operation = [PINOperation operationWithBlock:block reference:reference priority:priority];
return operation.reference;
}

- (id<PINOperationReference>)addOperation:(PINOperationBlock)block withPriority:(PINOperationQueuePriority)priority identifier:(NSString *)identifier data:(id)data dataCoallescingBlock:(PINOperationDataCoallescingBlock)dataCoallescingBlock completion:(dispatch_block_t)completion
{
PINOperation *operation = nil;
BOOL isNewOperation = NO;

[self lock];
dispatch_group_enter(_group);
[queue addObject:operation];
[_queuedOperations addObject:operation];
[_referenceToOperations setObject:operation forKey:reference];
if (identifier != nil && (operation = [_identifierToOperations objectForKey:identifier]) != nil) {
// There is an exisiting operation with the provided identifier, let's coallescing these operations
if (dataCoallescingBlock != nil) {
operation.data = dataCoallescingBlock(operation.data, data);
}

if (completion != nil) {
dispatch_block_t initialCompletion = operation.completion;
if (initialCompletion == nil) {
operation.completion = completion;
} else {
operation.completion = ^{
initialCompletion();
completion();
};
}
}
} else {
isNewOperation = YES;
operation = [PINOperation operationWithBlock:block
reference:[self nextOperationReference]
priority:priority
identifier:identifier
data:data
completion:completion];
[self locked_addOperation:operation];
}
[self unlock];

[self scheduleNextOperations:NO];
if (isNewOperation) {
[self scheduleNextOperations:NO];
}

return reference;
return operation.reference;
}

- (void)locked_addOperation:(PINOperation *)operation
{
NSMutableOrderedSet *queue = [self operationQueueWithPriority:operation.priority];

dispatch_group_enter(_group);
[queue addObject:operation];
[_queuedOperations addObject:operation];
[_referenceToOperations setObject:operation forKey:operation.reference];
if (operation.identifier != nil) {
[_identifierToOperations setObject:operation forKey:operation.identifier];
}
}

- (void)cancelAllOperations
Expand Down Expand Up @@ -213,7 +273,10 @@ - (void)scheduleNextOperations:(BOOL)onlyCheckSerial
if (operation) {
_serialQueueBusy = YES;
dispatch_async(_serialQueue, ^{
operation.block();
operation.block(operation.data);
if (operation.completion) {
operation.completion();
}
dispatch_group_leave(_group);

[self lock];
Expand All @@ -239,7 +302,10 @@ - (void)scheduleNextOperations:(BOOL)onlyCheckSerial

if (operation) {
dispatch_async(_concurrentQueue, ^{
operation.block();
operation.block(operation.data);
if (operation.completion) {
operation.completion();
}
dispatch_group_leave(_group);
dispatch_semaphore_signal(_concurrentSemaphore);
});
Expand Down

0 comments on commit bceac42

Please sign in to comment.