Skip to content

Commit

Permalink
Support operation coallescing (#141)
Browse files Browse the repository at this point in the history
* Disk cache removes its trash directory as a whole instead of removing files inside one by one

* Make sharedTrashURL thread-safe

* Support operation coallescing

* Add unit tests for operation coallescing

* Address Garrett's feedbacks

* Update tests
  • Loading branch information
nguyenhuy authored and garrettmoon committed Nov 17, 2016
1 parent b700ec2 commit 48031ad
Show file tree
Hide file tree
Showing 5 changed files with 409 additions and 60 deletions.
140 changes: 95 additions & 45 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 PINDiskTrimmingSizeCoallescingBlock = ^id(NSNumber *existingSize, NSNumber *newSize) {
NSComparisonResult result = [existingSize compare:newSize];
return (result == NSOrderedDescending) ? newSize : existingSize;
};

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

@interface PINDiskCache () {
NSConditionLock *_instanceLock;

Expand All @@ -40,6 +54,8 @@ @interface PINDiskCache () {

@implementation PINDiskCache

static NSURL *_sharedTrashURL;

@synthesize willAddObjectBlock = _willAddObjectBlock;
@synthesize willRemoveObjectBlock = _willRemoveObjectBlock;
@synthesize willRemoveAllObjectsBlock = _willRemoveAllObjectsBlock;
Expand Down Expand Up @@ -268,25 +284,36 @@ + (dispatch_queue_t)sharedTrashQueue
return trashQueue;
}

+ (NSLock *)sharedLock
{
static NSLock *sharedLock;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedLock = [NSLock new];
});
return sharedLock;
}

+ (NSURL *)sharedTrashURL
{
static NSURL *sharedTrashURL;
static dispatch_once_t predicate;
NSURL *trashURL;

dispatch_once(&predicate, ^{
sharedTrashURL = [[[NSURL alloc] initFileURLWithPath:NSTemporaryDirectory()] URLByAppendingPathComponent:PINDiskCachePrefix isDirectory:YES];

if (![[NSFileManager defaultManager] fileExistsAtPath:[sharedTrashURL path]]) {
[[PINDiskCache sharedLock] lock];
if (_sharedTrashURL == nil) {
NSString *uniqueString = [[NSProcessInfo processInfo] globallyUniqueString];
_sharedTrashURL = [[[NSURL alloc] initFileURLWithPath:NSTemporaryDirectory()] URLByAppendingPathComponent:uniqueString isDirectory:YES];

NSError *error = nil;
[[NSFileManager defaultManager] createDirectoryAtURL:sharedTrashURL
[[NSFileManager defaultManager] createDirectoryAtURL:_sharedTrashURL
withIntermediateDirectories:YES
attributes:nil
error:&error];
PINDiskCacheError(error);
}
});
trashURL = _sharedTrashURL;
[[PINDiskCache sharedLock] unlock];

return sharedTrashURL;
return trashURL;
}

+ (BOOL)moveItemAtURLToTrash:(NSURL *)itemURL
Expand All @@ -304,17 +331,19 @@ + (BOOL)moveItemAtURLToTrash:(NSURL *)itemURL

+ (void)emptyTrash
{
dispatch_async([self sharedTrashQueue], ^{
NSError *searchTrashedItemsError = nil;
NSArray *trashedItems = [[NSFileManager defaultManager] contentsOfDirectoryAtURL:[self sharedTrashURL]
includingPropertiesForKeys:nil
options:0
error:&searchTrashedItemsError];
PINDiskCacheError(searchTrashedItemsError);
dispatch_async([PINDiskCache sharedTrashQueue], ^{
NSURL *trashURL;

[[PINDiskCache sharedLock] lock];
if (_sharedTrashURL != nil) {
trashURL = _sharedTrashURL;
_sharedTrashURL = nil;
}
[[PINDiskCache sharedLock] unlock];

for (NSURL *trashedItemURL in trashedItems) {
if (trashURL != nil) {
NSError *removeTrashedItemError = nil;
[[NSFileManager defaultManager] removeItemAtURL:trashedItemURL error:&removeTrashedItemError];
[[NSFileManager defaultManager] removeItemAtURL:trashURL error:&removeTrashedItemError];
PINDiskCacheError(removeTrashedItemError);
}
});
Expand Down Expand Up @@ -631,44 +660,65 @@ - (void)removeObjectForKey:(NSString *)key block:(PINDiskCacheObjectBlock)block

- (void)trimToSize:(NSUInteger)trimByteCount block:(PINDiskCacheBlock)block
{
__weak PINDiskCache *weakSelf = self;
PINOperationBlock operation = ^(id data) {
[self trimToSize:((NSNumber *)data).unsignedIntegerValue];
};

dispatch_block_t completion = nil;
if (block) {
completion = ^{
block(self);
};
}

[self.operationQueue addOperation:^{
PINDiskCache *strongSelf = weakSelf;
[strongSelf trimToSize:trimByteCount];

if (block) {
block(strongSelf);
}
} withPriority:PINOperationQueuePriorityLow];
[self.operationQueue addOperation:operation
withPriority:PINOperationQueuePriorityLow
identifier:PINDiskCacheOperationIdentifierTrimToSize
coalescingData:[NSNumber numberWithUnsignedInteger:trimByteCount]
dataCoallescingBlock:PINDiskTrimmingSizeCoallescingBlock
completion:completion];
}

- (void)trimToDate:(NSDate *)trimDate block:(PINDiskCacheBlock)block
{
__weak PINDiskCache *weakSelf = self;
PINOperationBlock operation = ^(id data){
[self trimToDate:(NSDate *)data];
};

[self.operationQueue addOperation:^{
PINDiskCache *strongSelf = weakSelf;
[strongSelf trimToDate:trimDate];

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

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

- (void)trimToSizeByDate:(NSUInteger)trimByteCount block:(PINDiskCacheBlock)block
{
__weak PINDiskCache *weakSelf = self;
PINOperationBlock operation = ^(id data){
[self trimToSizeByDate:((NSNumber *)data).unsignedIntegerValue];
};

[self.operationQueue addOperation:^{
PINDiskCache *strongSelf = weakSelf;
[strongSelf trimToSizeByDate:trimByteCount];

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

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

- (void)removeAllObjects:(PINDiskCacheBlock)block
Expand Down
9 changes: 9 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,12 @@ 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
coalescingData:(nullable id)coalescingData
dataCoallescingBlock:(nullable PINOperationDataCoallescingBlock)dataCoallescingBlock
completion:(nullable dispatch_block_t)completion;

/**
* Marks the operation as cancelled
Expand Down
Loading

0 comments on commit 48031ad

Please sign in to comment.