Authored by Thomas Rasch

o Changed the tile and memory cache to use dispatch queues

... ... @@ -30,10 +30,6 @@
#import "RMTileCache.h"
@interface RMMemoryCache : NSObject <RMTileCache>
{
NSMutableDictionary *cache;
NSUInteger capacity;
}
- (id)initWithCapacity:(NSUInteger)aCapacity;
... ...
... ... @@ -29,6 +29,11 @@
#import "RMTileImage.h"
@implementation RMMemoryCache
{
NSMutableDictionary *_memoryCache;
dispatch_queue_t _memoryCacheQueue;
NSUInteger _memoryCacheCapacity;
}
- (id)initWithCapacity:(NSUInteger)aCapacity
{
... ... @@ -37,12 +42,13 @@
RMLog(@"initializing memory cache %@ with capacity %d", self, aCapacity);
cache = [[NSMutableDictionary alloc] initWithCapacity:aCapacity];
_memoryCache = [[NSMutableDictionary alloc] initWithCapacity:aCapacity];
_memoryCacheQueue = dispatch_queue_create("routeme.memoryCacheQueue", DISPATCH_QUEUE_CONCURRENT);
if (aCapacity < 1)
aCapacity = 1;
capacity = aCapacity;
_memoryCacheCapacity = aCapacity;
return self;
}
... ... @@ -54,11 +60,10 @@
- (void)dealloc
{
@synchronized (cache)
{
[cache removeAllObjects];
[cache release]; cache = nil;
}
dispatch_barrier_sync(_memoryCacheQueue, ^{
[_memoryCache removeAllObjects];
[_memoryCache release]; _memoryCache = nil;
});
[super dealloc];
}
... ... @@ -67,60 +72,67 @@
{
LogMethod();
@synchronized (cache)
{
[cache removeAllObjects];
}
dispatch_barrier_async(_memoryCacheQueue, ^{
[_memoryCache removeAllObjects];
});
}
- (void)removeTile:(RMTile)tile
{
@synchronized (cache)
{
[cache removeObjectForKey:[RMTileCache tileHash:tile]];
}
dispatch_barrier_async(_memoryCacheQueue, ^{
[_memoryCache removeObjectForKey:[RMTileCache tileHash:tile]];
});
}
- (UIImage *)cachedImage:(RMTile)tile withCacheKey:(NSString *)aCacheKey
{
// RMLog(@"Memory cache check tile %d %d %d (%@)", tile.x, tile.y, tile.zoom, [RMTileCache tileHash:tile]);
RMCacheObject *cachedObject = nil;
__block RMCacheObject *cachedObject = nil;
NSNumber *tileHash = [RMTileCache tileHash:tile];
@synchronized (cache)
{
cachedObject = [cache objectForKey:tileHash];
if (!cachedObject)
return nil;
dispatch_sync(_memoryCacheQueue, ^{
cachedObject = [[_memoryCache objectForKey:tileHash] retain];
if (![[cachedObject cacheKey] isEqualToString:aCacheKey])
if (cachedObject)
{
[cache removeObjectForKey:tileHash];
return nil;
if ([[cachedObject cacheKey] isEqualToString:aCacheKey])
{
[cachedObject touch];
}
else
{
dispatch_barrier_async(_memoryCacheQueue, ^{
[_memoryCache removeObjectForKey:tileHash];
});
[cachedObject touch];
[cachedObject release]; cachedObject = nil;
}
}
});
// RMLog(@"Memory cache hit tile %d %d %d (%@)", tile.x, tile.y, tile.zoom, [RMTileCache tileHash:tile]);
[cachedObject autorelease];
return [cachedObject cachedObject];
}
/// Remove the least-recently used image from cache, if cache is at or over capacity. Removes only 1 image.
- (void)makeSpaceInCache
{
@synchronized (cache)
{
while ([cache count] >= capacity)
dispatch_barrier_async(_memoryCacheQueue, ^{
while ([_memoryCache count] >= _memoryCacheCapacity)
{
// Rather than scanning I would really like to be using a priority queue
// backed by a heap here.
// Maybe deleting one random element would work as well.
NSEnumerator *enumerator = [cache objectEnumerator];
NSEnumerator *enumerator = [_memoryCache objectEnumerator];
RMCacheObject *image;
NSDate *oldestDate = nil;
... ... @@ -138,10 +150,11 @@
if (oldestImage)
{
// RMLog(@"Memory cache delete tile %d %d %d (%@)", oldestImage.tile.x, oldestImage.tile.y, oldestImage.tile.zoom, [RMTileCache tileHash:oldestImage.tile]);
[cache removeObjectForKey:[RMTileCache tileHash:oldestImage.tile]];
}
[_memoryCache removeObjectForKey:[RMTileCache tileHash:oldestImage.tile]];
}
}
});
}
- (void)addImage:(UIImage *)image forTile:(RMTile)tile withCacheKey:(NSString *)aCacheKey
... ... @@ -150,20 +163,18 @@
[self makeSpaceInCache];
@synchronized (cache)
{
[cache setObject:[RMCacheObject cacheObject:image forTile:tile withCacheKey:aCacheKey] forKey:[RMTileCache tileHash:tile]];
}
dispatch_barrier_async(_memoryCacheQueue, ^{
[_memoryCache setObject:[RMCacheObject cacheObject:image forTile:tile withCacheKey:aCacheKey] forKey:[RMTileCache tileHash:tile]];
});
}
- (void)removeAllCachedImages
{
LogMethod();
@synchronized (cache)
{
[cache removeAllObjects];
}
dispatch_barrier_async(_memoryCacheQueue, ^{
[_memoryCache removeAllObjects];
});
}
@end
... ...
... ... @@ -58,15 +58,6 @@ typedef enum {
#pragma mark -
@interface RMTileCache : NSObject <RMTileCache>
{
NSMutableArray *caches;
// The memory cache, if we have one
// This one has its own variable because we want to propagate cache hits down in
// the cache hierarchy up to the memory cache
RMMemoryCache *memoryCache;
NSTimeInterval expiryPeriod;
}
- (id)initWithExpiryPeriod:(NSTimeInterval)period;
... ...
... ... @@ -40,15 +40,28 @@
@end
@implementation RMTileCache
{
NSMutableArray *_tileCaches;
// The memory cache, if we have one
// This one has its own variable because we want to propagate cache hits down in
// the cache hierarchy up to the memory cache
RMMemoryCache *_memoryCache;
NSTimeInterval _expiryPeriod;
dispatch_queue_t _tileCacheQueue;
}
- (id)initWithExpiryPeriod:(NSTimeInterval)period
{
if (!(self = [super init]))
return nil;
caches = [[NSMutableArray alloc] init];
memoryCache = nil;
expiryPeriod = period;
_tileCaches = [[NSMutableArray alloc] init];
_tileCacheQueue = dispatch_queue_create("routeme.tileCacheQueue", DISPATCH_QUEUE_CONCURRENT);
_memoryCache = nil;
_expiryPeriod = period;
id cacheCfg = [[RMConfiguration configuration] cacheConfiguration];
if (!cacheCfg)
... ... @@ -67,7 +80,7 @@
if ([@"memory-cache" isEqualToString:type])
{
memoryCache = [[self memoryCacheWithConfig:cfg] retain];
_memoryCache = [[self memoryCacheWithConfig:cfg] retain];
continue;
}
... ... @@ -75,7 +88,7 @@
newCache = [self databaseCacheWithConfig:cfg];
if (newCache)
[caches addObject:newCache];
[_tileCaches addObject:newCache];
else
RMLog(@"failed to create cache of type %@", type);
... ... @@ -98,17 +111,19 @@
- (void)dealloc
{
[memoryCache release]; memoryCache = nil;
[caches release]; caches = nil;
dispatch_barrier_sync(_tileCacheQueue, ^{
[_memoryCache release]; _memoryCache = nil;
[_tileCaches release]; _tileCaches = nil;
});
[super dealloc];
}
- (void)addCache:(id <RMTileCache>)cache
{
@synchronized (caches)
{
[caches addObject:cache];
}
dispatch_barrier_async(_tileCacheQueue, ^{
[_tileCaches addObject:cache];
});
}
+ (NSNumber *)tileHash:(RMTile)tile
... ... @@ -119,26 +134,27 @@
// Returns the cached image if it exists. nil otherwise.
- (UIImage *)cachedImage:(RMTile)tile withCacheKey:(NSString *)aCacheKey
{
UIImage *image = [memoryCache cachedImage:tile withCacheKey:aCacheKey];
__block UIImage *image = [_memoryCache cachedImage:tile withCacheKey:aCacheKey];
if (image)
return image;
@synchronized (caches)
{
for (id <RMTileCache> cache in caches)
dispatch_sync(_tileCacheQueue, ^{
for (id <RMTileCache> cache in _tileCaches)
{
image = [cache cachedImage:tile withCacheKey:aCacheKey];
image = [[cache cachedImage:tile withCacheKey:aCacheKey] retain];
if (image != nil)
{
[memoryCache addImage:image forTile:tile withCacheKey:aCacheKey];
return image;
}
[_memoryCache addImage:image forTile:tile withCacheKey:aCacheKey];
break;
}
}
return nil;
});
return [image autorelease];
}
- (void)addImage:(UIImage *)image forTile:(RMTile)tile withCacheKey:(NSString *)aCacheKey
... ... @@ -146,43 +162,47 @@
if (!image || !aCacheKey)
return;
[memoryCache addImage:image forTile:tile withCacheKey:aCacheKey];
[_memoryCache addImage:image forTile:tile withCacheKey:aCacheKey];
@synchronized (caches)
{
for (id <RMTileCache> cache in caches)
dispatch_sync(_tileCacheQueue, ^{
for (id <RMTileCache> cache in _tileCaches)
{
if ([cache respondsToSelector:@selector(addImage:forTile:withCacheKey:)])
[cache addImage:image forTile:tile withCacheKey:aCacheKey];
}
}
});
}
- (void)didReceiveMemoryWarning
{
LogMethod();
[memoryCache didReceiveMemoryWarning];
@synchronized (caches)
{
for (id<RMTileCache> cache in caches)
[_memoryCache didReceiveMemoryWarning];
dispatch_sync(_tileCacheQueue, ^{
for (id<RMTileCache> cache in _tileCaches)
{
[cache didReceiveMemoryWarning];
}
}
});
}
- (void)removeAllCachedImages
{
[memoryCache removeAllCachedImages];
[_memoryCache removeAllCachedImages];
@synchronized (caches)
{
for (id<RMTileCache> cache in caches)
dispatch_sync(_tileCacheQueue, ^{
for (id<RMTileCache> cache in _tileCaches)
{
[cache removeAllCachedImages];
}
}
});
}
@end
... ... @@ -245,13 +265,13 @@
NSNumber *expiryPeriodNumber = [cfg objectForKey:@"expiryPeriod"];
if (expiryPeriodNumber != nil)
expiryPeriod = [expiryPeriodNumber intValue];
_expiryPeriod = [expiryPeriodNumber intValue];
RMDatabaseCache *dbCache = [[[RMDatabaseCache alloc] initUsingCacheDir:useCacheDir] autorelease];
[dbCache setCapacity:capacity];
[dbCache setPurgeStrategy:strategy];
[dbCache setMinimalPurge:minimalPurge];
[dbCache setExpiryPeriod:expiryPeriod];
[dbCache setExpiryPeriod:_expiryPeriod];
return dbCache;
}
... ...