Authored by Thomas Rasch

o Changed the tile and memory cache to use dispatch queues

@@ -30,10 +30,6 @@ @@ -30,10 +30,6 @@
30 #import "RMTileCache.h" 30 #import "RMTileCache.h"
31 31
32 @interface RMMemoryCache : NSObject <RMTileCache> 32 @interface RMMemoryCache : NSObject <RMTileCache>
33 -{  
34 - NSMutableDictionary *cache;  
35 - NSUInteger capacity;  
36 -}  
37 33
38 - (id)initWithCapacity:(NSUInteger)aCapacity; 34 - (id)initWithCapacity:(NSUInteger)aCapacity;
39 35
@@ -29,6 +29,11 @@ @@ -29,6 +29,11 @@
29 #import "RMTileImage.h" 29 #import "RMTileImage.h"
30 30
31 @implementation RMMemoryCache 31 @implementation RMMemoryCache
  32 +{
  33 + NSMutableDictionary *_memoryCache;
  34 + dispatch_queue_t _memoryCacheQueue;
  35 + NSUInteger _memoryCacheCapacity;
  36 +}
32 37
33 - (id)initWithCapacity:(NSUInteger)aCapacity 38 - (id)initWithCapacity:(NSUInteger)aCapacity
34 { 39 {
@@ -37,12 +42,13 @@ @@ -37,12 +42,13 @@
37 42
38 RMLog(@"initializing memory cache %@ with capacity %d", self, aCapacity); 43 RMLog(@"initializing memory cache %@ with capacity %d", self, aCapacity);
39 44
40 - cache = [[NSMutableDictionary alloc] initWithCapacity:aCapacity]; 45 + _memoryCache = [[NSMutableDictionary alloc] initWithCapacity:aCapacity];
  46 + _memoryCacheQueue = dispatch_queue_create("routeme.memoryCacheQueue", DISPATCH_QUEUE_CONCURRENT);
41 47
42 if (aCapacity < 1) 48 if (aCapacity < 1)
43 aCapacity = 1; 49 aCapacity = 1;
44 50
45 - capacity = aCapacity; 51 + _memoryCacheCapacity = aCapacity;
46 52
47 return self; 53 return self;
48 } 54 }
@@ -54,11 +60,10 @@ @@ -54,11 +60,10 @@
54 60
55 - (void)dealloc 61 - (void)dealloc
56 { 62 {
57 - @synchronized (cache)  
58 - {  
59 - [cache removeAllObjects];  
60 - [cache release]; cache = nil;  
61 - } 63 + dispatch_barrier_sync(_memoryCacheQueue, ^{
  64 + [_memoryCache removeAllObjects];
  65 + [_memoryCache release]; _memoryCache = nil;
  66 + });
62 67
63 [super dealloc]; 68 [super dealloc];
64 } 69 }
@@ -67,60 +72,67 @@ @@ -67,60 +72,67 @@
67 { 72 {
68 LogMethod(); 73 LogMethod();
69 74
70 - @synchronized (cache)  
71 - {  
72 - [cache removeAllObjects];  
73 - } 75 + dispatch_barrier_async(_memoryCacheQueue, ^{
  76 + [_memoryCache removeAllObjects];
  77 + });
74 } 78 }
75 79
76 - (void)removeTile:(RMTile)tile 80 - (void)removeTile:(RMTile)tile
77 { 81 {
78 - @synchronized (cache)  
79 - {  
80 - [cache removeObjectForKey:[RMTileCache tileHash:tile]];  
81 - } 82 + dispatch_barrier_async(_memoryCacheQueue, ^{
  83 + [_memoryCache removeObjectForKey:[RMTileCache tileHash:tile]];
  84 + });
82 } 85 }
83 86
84 - (UIImage *)cachedImage:(RMTile)tile withCacheKey:(NSString *)aCacheKey 87 - (UIImage *)cachedImage:(RMTile)tile withCacheKey:(NSString *)aCacheKey
85 { 88 {
86 // RMLog(@"Memory cache check tile %d %d %d (%@)", tile.x, tile.y, tile.zoom, [RMTileCache tileHash:tile]); 89 // RMLog(@"Memory cache check tile %d %d %d (%@)", tile.x, tile.y, tile.zoom, [RMTileCache tileHash:tile]);
87 90
88 - RMCacheObject *cachedObject = nil; 91 + __block RMCacheObject *cachedObject = nil;
89 NSNumber *tileHash = [RMTileCache tileHash:tile]; 92 NSNumber *tileHash = [RMTileCache tileHash:tile];
90 93
91 - @synchronized (cache)  
92 - {  
93 - cachedObject = [cache objectForKey:tileHash];  
94 - if (!cachedObject)  
95 - return nil; 94 + dispatch_sync(_memoryCacheQueue, ^{
  95 +
  96 + cachedObject = [[_memoryCache objectForKey:tileHash] retain];
96 97
97 - if (![[cachedObject cacheKey] isEqualToString:aCacheKey]) 98 + if (cachedObject)
98 { 99 {
99 - [cache removeObjectForKey:tileHash];  
100 - return nil; 100 + if ([[cachedObject cacheKey] isEqualToString:aCacheKey])
  101 + {
  102 + [cachedObject touch];
101 } 103 }
  104 + else
  105 + {
  106 + dispatch_barrier_async(_memoryCacheQueue, ^{
  107 + [_memoryCache removeObjectForKey:tileHash];
  108 + });
102 109
103 - [cachedObject touch]; 110 + [cachedObject release]; cachedObject = nil;
104 } 111 }
  112 + }
  113 +
  114 + });
105 115
106 // RMLog(@"Memory cache hit tile %d %d %d (%@)", tile.x, tile.y, tile.zoom, [RMTileCache tileHash:tile]); 116 // RMLog(@"Memory cache hit tile %d %d %d (%@)", tile.x, tile.y, tile.zoom, [RMTileCache tileHash:tile]);
107 117
  118 + [cachedObject autorelease];
  119 +
108 return [cachedObject cachedObject]; 120 return [cachedObject cachedObject];
109 } 121 }
110 122
111 /// Remove the least-recently used image from cache, if cache is at or over capacity. Removes only 1 image. 123 /// Remove the least-recently used image from cache, if cache is at or over capacity. Removes only 1 image.
112 - (void)makeSpaceInCache 124 - (void)makeSpaceInCache
113 { 125 {
114 - @synchronized (cache)  
115 - {  
116 - while ([cache count] >= capacity) 126 + dispatch_barrier_async(_memoryCacheQueue, ^{
  127 +
  128 + while ([_memoryCache count] >= _memoryCacheCapacity)
117 { 129 {
118 // Rather than scanning I would really like to be using a priority queue 130 // Rather than scanning I would really like to be using a priority queue
119 // backed by a heap here. 131 // backed by a heap here.
120 132
121 // Maybe deleting one random element would work as well. 133 // Maybe deleting one random element would work as well.
122 134
123 - NSEnumerator *enumerator = [cache objectEnumerator]; 135 + NSEnumerator *enumerator = [_memoryCache objectEnumerator];
124 RMCacheObject *image; 136 RMCacheObject *image;
125 137
126 NSDate *oldestDate = nil; 138 NSDate *oldestDate = nil;
@@ -138,10 +150,11 @@ @@ -138,10 +150,11 @@
138 if (oldestImage) 150 if (oldestImage)
139 { 151 {
140 // RMLog(@"Memory cache delete tile %d %d %d (%@)", oldestImage.tile.x, oldestImage.tile.y, oldestImage.tile.zoom, [RMTileCache tileHash:oldestImage.tile]); 152 // RMLog(@"Memory cache delete tile %d %d %d (%@)", oldestImage.tile.x, oldestImage.tile.y, oldestImage.tile.zoom, [RMTileCache tileHash:oldestImage.tile]);
141 - [cache removeObjectForKey:[RMTileCache tileHash:oldestImage.tile]];  
142 - } 153 + [_memoryCache removeObjectForKey:[RMTileCache tileHash:oldestImage.tile]];
143 } 154 }
144 } 155 }
  156 +
  157 + });
145 } 158 }
146 159
147 - (void)addImage:(UIImage *)image forTile:(RMTile)tile withCacheKey:(NSString *)aCacheKey 160 - (void)addImage:(UIImage *)image forTile:(RMTile)tile withCacheKey:(NSString *)aCacheKey
@@ -150,20 +163,18 @@ @@ -150,20 +163,18 @@
150 163
151 [self makeSpaceInCache]; 164 [self makeSpaceInCache];
152 165
153 - @synchronized (cache)  
154 - {  
155 - [cache setObject:[RMCacheObject cacheObject:image forTile:tile withCacheKey:aCacheKey] forKey:[RMTileCache tileHash:tile]];  
156 - } 166 + dispatch_barrier_async(_memoryCacheQueue, ^{
  167 + [_memoryCache setObject:[RMCacheObject cacheObject:image forTile:tile withCacheKey:aCacheKey] forKey:[RMTileCache tileHash:tile]];
  168 + });
157 } 169 }
158 170
159 - (void)removeAllCachedImages 171 - (void)removeAllCachedImages
160 { 172 {
161 LogMethod(); 173 LogMethod();
162 174
163 - @synchronized (cache)  
164 - {  
165 - [cache removeAllObjects];  
166 - } 175 + dispatch_barrier_async(_memoryCacheQueue, ^{
  176 + [_memoryCache removeAllObjects];
  177 + });
167 } 178 }
168 179
169 @end 180 @end
@@ -58,15 +58,6 @@ typedef enum { @@ -58,15 +58,6 @@ typedef enum {
58 #pragma mark - 58 #pragma mark -
59 59
60 @interface RMTileCache : NSObject <RMTileCache> 60 @interface RMTileCache : NSObject <RMTileCache>
61 -{  
62 - NSMutableArray *caches;  
63 -  
64 - // The memory cache, if we have one  
65 - // This one has its own variable because we want to propagate cache hits down in  
66 - // the cache hierarchy up to the memory cache  
67 - RMMemoryCache *memoryCache;  
68 - NSTimeInterval expiryPeriod;  
69 -}  
70 61
71 - (id)initWithExpiryPeriod:(NSTimeInterval)period; 62 - (id)initWithExpiryPeriod:(NSTimeInterval)period;
72 63
@@ -40,15 +40,28 @@ @@ -40,15 +40,28 @@
40 @end 40 @end
41 41
42 @implementation RMTileCache 42 @implementation RMTileCache
  43 +{
  44 + NSMutableArray *_tileCaches;
  45 +
  46 + // The memory cache, if we have one
  47 + // This one has its own variable because we want to propagate cache hits down in
  48 + // the cache hierarchy up to the memory cache
  49 + RMMemoryCache *_memoryCache;
  50 + NSTimeInterval _expiryPeriod;
  51 +
  52 + dispatch_queue_t _tileCacheQueue;
  53 +}
43 54
44 - (id)initWithExpiryPeriod:(NSTimeInterval)period 55 - (id)initWithExpiryPeriod:(NSTimeInterval)period
45 { 56 {
46 if (!(self = [super init])) 57 if (!(self = [super init]))
47 return nil; 58 return nil;
48 59
49 - caches = [[NSMutableArray alloc] init];  
50 - memoryCache = nil;  
51 - expiryPeriod = period; 60 + _tileCaches = [[NSMutableArray alloc] init];
  61 + _tileCacheQueue = dispatch_queue_create("routeme.tileCacheQueue", DISPATCH_QUEUE_CONCURRENT);
  62 +
  63 + _memoryCache = nil;
  64 + _expiryPeriod = period;
52 65
53 id cacheCfg = [[RMConfiguration configuration] cacheConfiguration]; 66 id cacheCfg = [[RMConfiguration configuration] cacheConfiguration];
54 if (!cacheCfg) 67 if (!cacheCfg)
@@ -67,7 +80,7 @@ @@ -67,7 +80,7 @@
67 80
68 if ([@"memory-cache" isEqualToString:type]) 81 if ([@"memory-cache" isEqualToString:type])
69 { 82 {
70 - memoryCache = [[self memoryCacheWithConfig:cfg] retain]; 83 + _memoryCache = [[self memoryCacheWithConfig:cfg] retain];
71 continue; 84 continue;
72 } 85 }
73 86
@@ -75,7 +88,7 @@ @@ -75,7 +88,7 @@
75 newCache = [self databaseCacheWithConfig:cfg]; 88 newCache = [self databaseCacheWithConfig:cfg];
76 89
77 if (newCache) 90 if (newCache)
78 - [caches addObject:newCache]; 91 + [_tileCaches addObject:newCache];
79 else 92 else
80 RMLog(@"failed to create cache of type %@", type); 93 RMLog(@"failed to create cache of type %@", type);
81 94
@@ -98,17 +111,19 @@ @@ -98,17 +111,19 @@
98 111
99 - (void)dealloc 112 - (void)dealloc
100 { 113 {
101 - [memoryCache release]; memoryCache = nil;  
102 - [caches release]; caches = nil; 114 + dispatch_barrier_sync(_tileCacheQueue, ^{
  115 + [_memoryCache release]; _memoryCache = nil;
  116 + [_tileCaches release]; _tileCaches = nil;
  117 + });
  118 +
103 [super dealloc]; 119 [super dealloc];
104 } 120 }
105 121
106 - (void)addCache:(id <RMTileCache>)cache 122 - (void)addCache:(id <RMTileCache>)cache
107 { 123 {
108 - @synchronized (caches)  
109 - {  
110 - [caches addObject:cache];  
111 - } 124 + dispatch_barrier_async(_tileCacheQueue, ^{
  125 + [_tileCaches addObject:cache];
  126 + });
112 } 127 }
113 128
114 + (NSNumber *)tileHash:(RMTile)tile 129 + (NSNumber *)tileHash:(RMTile)tile
@@ -119,26 +134,27 @@ @@ -119,26 +134,27 @@
119 // Returns the cached image if it exists. nil otherwise. 134 // Returns the cached image if it exists. nil otherwise.
120 - (UIImage *)cachedImage:(RMTile)tile withCacheKey:(NSString *)aCacheKey 135 - (UIImage *)cachedImage:(RMTile)tile withCacheKey:(NSString *)aCacheKey
121 { 136 {
122 - UIImage *image = [memoryCache cachedImage:tile withCacheKey:aCacheKey]; 137 + __block UIImage *image = [_memoryCache cachedImage:tile withCacheKey:aCacheKey];
123 138
124 if (image) 139 if (image)
125 return image; 140 return image;
126 141
127 - @synchronized (caches)  
128 - {  
129 - for (id <RMTileCache> cache in caches) 142 + dispatch_sync(_tileCacheQueue, ^{
  143 +
  144 + for (id <RMTileCache> cache in _tileCaches)
130 { 145 {
131 - image = [cache cachedImage:tile withCacheKey:aCacheKey]; 146 + image = [[cache cachedImage:tile withCacheKey:aCacheKey] retain];
132 147
133 if (image != nil) 148 if (image != nil)
134 { 149 {
135 - [memoryCache addImage:image forTile:tile withCacheKey:aCacheKey];  
136 - return image;  
137 - } 150 + [_memoryCache addImage:image forTile:tile withCacheKey:aCacheKey];
  151 + break;
138 } 152 }
139 } 153 }
140 154
141 - return nil; 155 + });
  156 +
  157 + return [image autorelease];
142 } 158 }
143 159
144 - (void)addImage:(UIImage *)image forTile:(RMTile)tile withCacheKey:(NSString *)aCacheKey 160 - (void)addImage:(UIImage *)image forTile:(RMTile)tile withCacheKey:(NSString *)aCacheKey
@@ -146,43 +162,47 @@ @@ -146,43 +162,47 @@
146 if (!image || !aCacheKey) 162 if (!image || !aCacheKey)
147 return; 163 return;
148 164
149 - [memoryCache addImage:image forTile:tile withCacheKey:aCacheKey]; 165 + [_memoryCache addImage:image forTile:tile withCacheKey:aCacheKey];
150 166
151 - @synchronized (caches)  
152 - {  
153 - for (id <RMTileCache> cache in caches) 167 + dispatch_sync(_tileCacheQueue, ^{
  168 +
  169 + for (id <RMTileCache> cache in _tileCaches)
154 { 170 {
155 if ([cache respondsToSelector:@selector(addImage:forTile:withCacheKey:)]) 171 if ([cache respondsToSelector:@selector(addImage:forTile:withCacheKey:)])
156 [cache addImage:image forTile:tile withCacheKey:aCacheKey]; 172 [cache addImage:image forTile:tile withCacheKey:aCacheKey];
157 } 173 }
158 - } 174 +
  175 + });
159 } 176 }
160 177
161 - (void)didReceiveMemoryWarning 178 - (void)didReceiveMemoryWarning
162 { 179 {
163 LogMethod(); 180 LogMethod();
164 - [memoryCache didReceiveMemoryWarning];  
165 181
166 - @synchronized (caches)  
167 - {  
168 - for (id<RMTileCache> cache in caches) 182 + [_memoryCache didReceiveMemoryWarning];
  183 +
  184 + dispatch_sync(_tileCacheQueue, ^{
  185 +
  186 + for (id<RMTileCache> cache in _tileCaches)
169 { 187 {
170 [cache didReceiveMemoryWarning]; 188 [cache didReceiveMemoryWarning];
171 } 189 }
172 - } 190 +
  191 + });
173 } 192 }
174 193
175 - (void)removeAllCachedImages 194 - (void)removeAllCachedImages
176 { 195 {
177 - [memoryCache removeAllCachedImages]; 196 + [_memoryCache removeAllCachedImages];
178 197
179 - @synchronized (caches)  
180 - {  
181 - for (id<RMTileCache> cache in caches) 198 + dispatch_sync(_tileCacheQueue, ^{
  199 +
  200 + for (id<RMTileCache> cache in _tileCaches)
182 { 201 {
183 [cache removeAllCachedImages]; 202 [cache removeAllCachedImages];
184 } 203 }
185 - } 204 +
  205 + });
186 } 206 }
187 207
188 @end 208 @end
@@ -245,13 +265,13 @@ @@ -245,13 +265,13 @@
245 265
246 NSNumber *expiryPeriodNumber = [cfg objectForKey:@"expiryPeriod"]; 266 NSNumber *expiryPeriodNumber = [cfg objectForKey:@"expiryPeriod"];
247 if (expiryPeriodNumber != nil) 267 if (expiryPeriodNumber != nil)
248 - expiryPeriod = [expiryPeriodNumber intValue]; 268 + _expiryPeriod = [expiryPeriodNumber intValue];
249 269
250 RMDatabaseCache *dbCache = [[[RMDatabaseCache alloc] initUsingCacheDir:useCacheDir] autorelease]; 270 RMDatabaseCache *dbCache = [[[RMDatabaseCache alloc] initUsingCacheDir:useCacheDir] autorelease];
251 [dbCache setCapacity:capacity]; 271 [dbCache setCapacity:capacity];
252 [dbCache setPurgeStrategy:strategy]; 272 [dbCache setPurgeStrategy:strategy];
253 [dbCache setMinimalPurge:minimalPurge]; 273 [dbCache setMinimalPurge:minimalPurge];
254 - [dbCache setExpiryPeriod:expiryPeriod]; 274 + [dbCache setExpiryPeriod:_expiryPeriod];
255 275
256 return dbCache; 276 return dbCache;
257 } 277 }