Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion SPTPersistentCache.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@
isa = PBXProject;
attributes = {
CLASSPREFIX = SPTPersistentCache;
LastUpgradeCheck = 0910;
LastUpgradeCheck = 0900;
ORGANIZATIONNAME = Spotify;
TargetAttributes = {
0510FF121BA2FF7A00ED0766 = {
Expand Down Expand Up @@ -492,7 +492,14 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 9C882A801C65422700D463BB /* project.xcconfig */;
buildSettings = {
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We want to put all of these in our xcconfig file. To keep the Xcode project clean and make it easier to maintain. See https://github.com/spotify/SPTPersistentCache/blob/master/ci/spotify_os.xcconfig

CLANG_WARN_COMMA = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_DYNAMIC_NO_PIC = NO;
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
HEADER_SEARCH_PATHS = (
Expand All @@ -501,27 +508,36 @@
);
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
VALID_ARCHS = "armv7 arm64 armv7k";
};
name = Debug;
};
0510FF261BA2FF7A00ED0766 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9C882A801C65422700D463BB /* project.xcconfig */;
buildSettings = {
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
HEADER_SEARCH_PATHS = (
include,
"$(inherited)",
);
MTL_ENABLE_DEBUG_INFO = NO;
VALIDATE_PRODUCT = YES;
VALID_ARCHS = "armv7 arm64 armv7k";
};
name = Release;
};
0510FF281BA2FF7A00ED0766 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
COMBINE_HIDPI_IMAGES = YES;
ONLY_ACTIVE_ARCH = NO;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0910"
LastUpgradeVersion = "0900"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
5 changes: 5 additions & 0 deletions Sources/SPTPersistentCache.m
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,11 @@ - (void)unscheduleGarbageCollector
[self.garbageCollector unschedule];
}

- (void)enqueueGarbageCollector
{
[self.garbageCollector enqueueGarbageCollection];
}

- (void)pruneWithCallback:(SPTPersistentCacheResponseCallback _Nullable)callback
onQueue:(dispatch_queue_t _Nullable)queue
{
Expand Down
4 changes: 1 addition & 3 deletions Sources/SPTPersistentCacheFileManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@
#import "SPTPersistentCacheDebugUtilities.h"
#import "SPTPersistentCacheOptions.h"

static const double SPTPersistentCacheFileManagerMinFreeDiskSpace = 0.1;

const NSUInteger SPTPersistentCacheFileManagerSubDirNameLength = 2;

@implementation SPTPersistentCacheFileManager
Expand Down Expand Up @@ -179,7 +177,7 @@ - (SPTPersistentCacheDiskSize)optimizedDiskSizeForCacheSize:(SPTPersistentCacheD
SPTPersistentCacheDiskSize totalSpace = fileSystemSize.longLongValue;
SPTPersistentCacheDiskSize freeSpace = fileSystemFreeSpace.longLongValue + currentCacheSize;
SPTPersistentCacheDiskSize proposedCacheSize = freeSpace - llrint(totalSpace *
SPTPersistentCacheFileManagerMinFreeDiskSpace);
self.options.minimumFreeDiskSpaceFraction);

tempCacheSize = MAX(0, proposedCacheSize);

Expand Down
5 changes: 5 additions & 0 deletions Sources/SPTPersistentCacheGarbageCollector.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,9 @@
*/
- (void)unschedule;

/**
* Enqueues the garbage collector. Called automatically if scheduled by the above functions.
*/
- (void)enqueueGarbageCollection;

@end
9 changes: 7 additions & 2 deletions Sources/SPTPersistentCacheGarbageCollector.m
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ - (void)dealloc

#pragma mark -

- (void)enqueueGarbageCollection:(NSTimer *)timer
- (void)enqueueGarbageCollection
{
__weak __typeof(self) const weakSelf = self;
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
Expand All @@ -81,6 +81,11 @@ - (void)enqueueGarbageCollection:(NSTimer *)timer
[self.queue addOperation:operation];
}

- (void)garbageCollectionTimerFired:(NSTimer *)timer
{
[self enqueueGarbageCollection];
}

- (void)schedule
{
if (!SPTPersistentCacheGarbageCollectorSchedulerIsInMainQueue()) {
Expand All @@ -100,7 +105,7 @@ - (void)schedule

self.timer = [NSTimer timerWithTimeInterval:self.options.garbageCollectionInterval
target:self
selector:@selector(enqueueGarbageCollection:)
selector:@selector(garbageCollectionTimerFired:)
userInfo:nil
repeats:YES];

Expand Down
3 changes: 3 additions & 0 deletions Sources/SPTPersistentCacheOptions.m
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
const NSUInteger SPTPersistentCacheDefaultExpirationTimeSec = 10 * 60;
const NSUInteger SPTPersistentCacheDefaultGCIntervalSec = 6 * 60 + 3;
static const NSUInteger SPTPersistentCacheDefaultCacheSizeInBytes = 0; // unbounded
const double SPTPersistentCacheDefaultMinFreeDiskSpaceFraction = 0.1; // 10% of total disk size

const NSUInteger SPTPersistentCacheMinimumGCIntervalLimit = 60;
const NSUInteger SPTPersistentCacheMinimumExpirationLimit = 60;
Expand Down Expand Up @@ -55,6 +56,7 @@ - (instancetype)init
_garbageCollectionInterval = SPTPersistentCacheDefaultGCIntervalSec;
_defaultExpirationPeriod = SPTPersistentCacheDefaultExpirationTimeSec;
_sizeConstraintBytes = SPTPersistentCacheDefaultCacheSizeInBytes;
_minimumFreeDiskSpaceFraction = SPTPersistentCacheDefaultMinFreeDiskSpaceFraction;
_maxConcurrentOperations = NSOperationQueueDefaultMaxConcurrentOperationCount;
_writePriority = NSOperationQueuePriorityNormal;
_writeQualityOfService = NSQualityOfServiceDefault;
Expand Down Expand Up @@ -117,6 +119,7 @@ - (id)copyWithZone:(NSZone *)zone
copy.garbageCollectionInterval = self.garbageCollectionInterval;
copy.defaultExpirationPeriod = self.defaultExpirationPeriod;
copy.sizeConstraintBytes = self.sizeConstraintBytes;
copy.minimumFreeDiskSpaceFraction = self.minimumFreeDiskSpaceFraction;

copy.debugOutput = self.debugOutput;
copy.timingCallback = self.timingCallback;
Expand Down
28 changes: 28 additions & 0 deletions Tests/SPTPersistentCacheFileManagerTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ - (void)setUp
SPTPersistentCacheOptions *options = [SPTPersistentCacheOptions new];
options.cachePath = SPTPersistentCacheFileManagerTestsCachePath;
options.cacheIdentifier = @"test";
options.sizeConstraintBytes = (SPTPersistentCacheDiskSize)1024 * 1024 * 1024 * 3; // 3 GiB
self.options = options;

self.cacheFileManager = [[SPTPersistentCacheFileManagerForTests alloc] initWithOptions:self.options];
Expand Down Expand Up @@ -168,6 +169,33 @@ - (void)testOptimizedDiskSizeForCacheSizeSmall
XCTAssertEqual(optimizedSize, (SPTPersistentCacheDiskSize)0);
}

- (void)testMinimumFreeDiskSpaceFraction
{
const SPTPersistentCacheDiskSize diskSize = (SPTPersistentCacheDiskSize)1024 * 1024 * 1024 * 16; // 16GiB
const SPTPersistentCacheDiskSize freeSpace = (SPTPersistentCacheDiskSize)1024 * 1024 * 1024 * 8; // 8GiB
NSFileManagerMock *fileManager = [NSFileManagerMock new];
fileManager.mock_attributesOfFileSystemForPaths = @{SPTPersistentCacheFileManagerTestsCachePath: @{NSFileSystemSize: @(diskSize),
NSFileSystemFreeSize: @(freeSpace)}};
self.cacheFileManager.test_fileManager = fileManager;

self.cacheFileManager.options.minimumFreeDiskSpaceFraction = 1.0;
SPTPersistentCacheDiskSize optimizedSize = [self.cacheFileManager optimizedDiskSizeForCacheSize:0];
XCTAssertEqual(optimizedSize, (SPTPersistentCacheDiskSize)0);

self.cacheFileManager.options.minimumFreeDiskSpaceFraction = 0.0;
optimizedSize = [self.cacheFileManager optimizedDiskSizeForCacheSize:0];
XCTAssertEqual(optimizedSize, (SPTPersistentCacheDiskSize)self.options.sizeConstraintBytes);

self.cacheFileManager.options.minimumFreeDiskSpaceFraction = 0.5;
optimizedSize = [self.cacheFileManager optimizedDiskSizeForCacheSize:0];
XCTAssertEqual(optimizedSize, (SPTPersistentCacheDiskSize)0);

const SPTPersistentCacheDiskSize twoGiB = (SPTPersistentCacheDiskSize)1024 * 1024 * 1024 * 2;
self.cacheFileManager.options.minimumFreeDiskSpaceFraction = (freeSpace - twoGiB) / (double)diskSize;
optimizedSize = [self.cacheFileManager optimizedDiskSizeForCacheSize:0];
XCTAssertEqual(optimizedSize, twoGiB);
}

- (void)testRemoveAllDataButKeysWithoutKeys
{
NSString *keyOne = @"AA";
Expand Down
21 changes: 19 additions & 2 deletions Tests/SPTPersistentCacheGarbageCollectorTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

@interface SPTPersistentCacheGarbageCollector ()
@property (nonatomic, strong) NSTimer *timer;
- (void)enqueueGarbageCollection:(NSTimer *)timer;
- (void)garbageCollectionTimerFired:(NSTimer *)timer;
@end


Expand Down Expand Up @@ -97,7 +97,7 @@ - (void)testGarbageCollectorEnqueue
dataCacheForUnitTests.queue = self.garbageCollector.queue;

dataCacheForUnitTests.testExpectation = expectation;
[self.garbageCollector enqueueGarbageCollection:nil];
[self.garbageCollector enqueueGarbageCollection];

[self waitForExpectationsWithTimeout:1.0 handler:^(NSError * _Nullable error) {
XCTAssertTrue(dataCacheForUnitTests.wasRunRegularGCCalled);
Expand All @@ -106,6 +106,23 @@ - (void)testGarbageCollectorEnqueue
}];
}

- (void)testGarbageCollectorTimerFired
{
__weak XCTestExpectation *expectation = [self expectationWithDescription:@"testGarbageCollectorEnqueue"];

SPTPersistentCacheForTimerProxyUnitTests *dataCacheForUnitTests = (SPTPersistentCacheForTimerProxyUnitTests *)self.garbageCollector.cache;
dataCacheForUnitTests.queue = self.garbageCollector.queue;

dataCacheForUnitTests.testExpectation = expectation;
[self.garbageCollector garbageCollectionTimerFired:nil];

[self waitForExpectationsWithTimeout:1.0 handler:^(NSError * _Nullable error) {
XCTAssertTrue(dataCacheForUnitTests.wasRunRegularGCCalled);
XCTAssertTrue(dataCacheForUnitTests.wasPruneBySizeCalled);
XCTAssertFalse(dataCacheForUnitTests.wasCalledFromIncorrectQueue);
}];
}

- (void)testIsGarbageCollectionScheduled
{
XCTAssertFalse(self.garbageCollector.isGarbageCollectionScheduled);
Expand Down
12 changes: 12 additions & 0 deletions Tests/SPTPersistentCacheTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -870,6 +870,18 @@ - (void)testUnscheduleGarbageCollection
XCTAssertFalse(cache.garbageCollector.isGarbageCollectionScheduled);
}

- (void)testEnqueueGargabeCollection
{
SPTPersistentCache *cache = [self createCacheWithTimeCallback:^NSTimeInterval{
// Exceed expiration interval by 1 sec
return kTestEpochTime + SPTPersistentCacheDefaultExpirationTimeSec + 1;
}
expirationTime:SPTPersistentCacheDefaultExpirationTimeSec];

[cache.garbageCollector enqueueGarbageCollection];
XCTAssertFalse(cache.garbageCollector.isGarbageCollectionScheduled);
}

/**
* This test also checks Req.#1.2 of cache API.
*/
Expand Down
5 changes: 5 additions & 0 deletions include/SPTPersistentCache/SPTPersistentCache.h
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,11 @@ typedef NSString * _Nonnull(^SPTPersistentCacheChooseKeyCallback)(NSArray<NSStri
* Stop garbage collection. If already stopped this method does nothing.
*/
- (void)unscheduleGarbageCollector;
/**
* Run the garbage collector right now. Use this if scheduling isn't appropriate,
* for example in applicationWillResignActive:
*/
- (void)enqueueGarbageCollector;
/**
* Delete all files files in managed folder unconditionaly.
* @param callback May be nil if not interested in result.
Expand Down
7 changes: 7 additions & 0 deletions include/SPTPersistentCache/SPTPersistentCacheOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,13 @@ FOUNDATION_EXPORT const NSUInteger SPTPersistentCacheMinimumExpirationLimit;
* @note Defaults to `0` (unbounded).
*/
@property (nonatomic, assign) NSUInteger sizeConstraintBytes;
/**
* The minimum fraction of free disk space required for caching. If there is less disk space
* available than this, the cache will be purged during garbage collection until the treshold
* is met. This could mean that the whole cache is evicted if the device is low on space.
* @note Defaults to `0.1`, 10% of the total disk size.
*/
@property (nonatomic, assign) double minimumFreeDiskSpaceFraction;
/**
* The queue priority for garbage collection. Defaults to NSOperationQueuePriorityLow.
*/
Expand Down