o Changed the tile and memory cache to use dispatch queues
Showing
4 changed files
with
138 additions
and
120 deletions
@@ -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,22 +29,28 @@ | @@ -29,22 +29,28 @@ | ||
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 | { |
35 | - if (!(self = [super init])) | ||
36 | - return nil; | 40 | + if (!(self = [super init])) |
41 | + return nil; | ||
42 | + | ||
43 | + RMLog(@"initializing memory cache %@ with capacity %d", self, aCapacity); | ||
37 | 44 | ||
38 | - RMLog(@"initializing memory cache %@ with capacity %d", self, aCapacity); | 45 | + _memoryCache = [[NSMutableDictionary alloc] initWithCapacity:aCapacity]; |
46 | + _memoryCacheQueue = dispatch_queue_create("routeme.memoryCacheQueue", DISPATCH_QUEUE_CONCURRENT); | ||
39 | 47 | ||
40 | - cache = [[NSMutableDictionary alloc] initWithCapacity:aCapacity]; | ||
41 | - | ||
42 | - if (aCapacity < 1) | ||
43 | - aCapacity = 1; | 48 | + if (aCapacity < 1) |
49 | + aCapacity = 1; | ||
44 | 50 | ||
45 | - capacity = aCapacity; | 51 | + _memoryCacheCapacity = aCapacity; |
46 | 52 | ||
47 | - return self; | 53 | + return self; |
48 | } | 54 | } |
49 | 55 | ||
50 | - (id)init | 56 | - (id)init |
@@ -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, ^{ |
96 | 95 | ||
97 | - if (![[cachedObject cacheKey] isEqualToString:aCacheKey]) | 96 | + cachedObject = [[_memoryCache objectForKey:tileHash] retain]; |
97 | + | ||
98 | + if (cachedObject) | ||
98 | { | 99 | { |
99 | - [cache removeObjectForKey:tileHash]; | ||
100 | - return nil; | 100 | + if ([[cachedObject cacheKey] isEqualToString:aCacheKey]) |
101 | + { | ||
102 | + [cachedObject touch]; | ||
103 | + } | ||
104 | + else | ||
105 | + { | ||
106 | + dispatch_barrier_async(_memoryCacheQueue, ^{ | ||
107 | + [_memoryCache removeObjectForKey:tileHash]; | ||
108 | + }); | ||
109 | + | ||
110 | + [cachedObject release]; cachedObject = nil; | ||
111 | + } | ||
101 | } | 112 | } |
102 | 113 | ||
103 | - [cachedObject touch]; | ||
104 | - } | 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]]; | 153 | + [_memoryCache removeObjectForKey:[RMTileCache tileHash:oldestImage.tile]]; |
142 | } | 154 | } |
143 | } | 155 | } |
144 | - } | 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,52 +40,65 @@ | @@ -40,52 +40,65 @@ | ||
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])) | ||
47 | - return nil; | 57 | + if (!(self = [super init])) |
58 | + return nil; | ||
48 | 59 | ||
49 | - caches = [[NSMutableArray alloc] init]; | ||
50 | - memoryCache = nil; | ||
51 | - expiryPeriod = period; | ||
52 | - | ||
53 | - id cacheCfg = [[RMConfiguration configuration] cacheConfiguration]; | ||
54 | - if (!cacheCfg) | ||
55 | - cacheCfg = [NSArray arrayWithObjects: | 60 | + _tileCaches = [[NSMutableArray alloc] init]; |
61 | + _tileCacheQueue = dispatch_queue_create("routeme.tileCacheQueue", DISPATCH_QUEUE_CONCURRENT); | ||
62 | + | ||
63 | + _memoryCache = nil; | ||
64 | + _expiryPeriod = period; | ||
65 | + | ||
66 | + id cacheCfg = [[RMConfiguration configuration] cacheConfiguration]; | ||
67 | + if (!cacheCfg) | ||
68 | + cacheCfg = [NSArray arrayWithObjects: | ||
56 | [NSDictionary dictionaryWithObject: @"memory-cache" forKey: @"type"], | 69 | [NSDictionary dictionaryWithObject: @"memory-cache" forKey: @"type"], |
57 | [NSDictionary dictionaryWithObject: @"db-cache" forKey: @"type"], | 70 | [NSDictionary dictionaryWithObject: @"db-cache" forKey: @"type"], |
58 | nil]; | 71 | nil]; |
59 | 72 | ||
60 | - for (id cfg in cacheCfg) | ||
61 | - { | ||
62 | - id <RMTileCache> newCache = nil; | 73 | + for (id cfg in cacheCfg) |
74 | + { | ||
75 | + id <RMTileCache> newCache = nil; | ||
63 | 76 | ||
64 | - @try { | 77 | + @try { |
65 | 78 | ||
66 | - NSString *type = [cfg valueForKey:@"type"]; | 79 | + NSString *type = [cfg valueForKey:@"type"]; |
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 | ||
74 | - if ([@"db-cache" isEqualToString:type]) | ||
75 | - newCache = [self databaseCacheWithConfig:cfg]; | 87 | + if ([@"db-cache" isEqualToString:type]) |
88 | + newCache = [self databaseCacheWithConfig:cfg]; | ||
76 | 89 | ||
77 | - if (newCache) | ||
78 | - [caches addObject:newCache]; | ||
79 | - else | ||
80 | - RMLog(@"failed to create cache of type %@", type); | 90 | + if (newCache) |
91 | + [_tileCaches addObject:newCache]; | ||
92 | + else | ||
93 | + RMLog(@"failed to create cache of type %@", type); | ||
81 | 94 | ||
82 | - } | ||
83 | - @catch (NSException * e) { | ||
84 | - RMLog(@"*** configuration error: %@", [e reason]); | ||
85 | - } | ||
86 | - } | 95 | + } |
96 | + @catch (NSException * e) { | ||
97 | + RMLog(@"*** configuration error: %@", [e reason]); | ||
98 | + } | ||
99 | + } | ||
87 | 100 | ||
88 | - return self; | 101 | + return self; |
89 | } | 102 | } |
90 | 103 | ||
91 | - (id)init | 104 | - (id)init |
@@ -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; | 150 | + [_memoryCache addImage:image forTile:tile withCacheKey:aCacheKey]; |
151 | + break; | ||
137 | } | 152 | } |
138 | } | 153 | } |
139 | - } | ||
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 | } |
-
Please register or login to post a comment