-
Notifications
You must be signed in to change notification settings - Fork 375
Description
When [TMCache objectForKey:block:] fails to find an object in memory, it falls back to the disk. If it finds the object on disk, it sets the object in memory. This makes sense.
However, if this is happening at the same time a new object is being set for the same key, there's a race to set the object in memory because [TMCache setObject:forKey:block:] sets the memory and disk caches in parallel.
Essentially, if calls to [TMCache objectForKey:block:] and [TMCache setObject:forKey:block:] are made in parallel, there's a chance the underlying calls land in this order:
- The get on the memory cache misses the object.
- The set on the memory cache sets the new object.
- The get on the disk cache finds the old object and it is re-set on the memory cache.
- The set on the disk cache sets the new object.
Until the memory cache is blown away, it will subsequently return the wrong value. This sample code reproduces the issue (it's a race, so obviously it won't repro every time):
TMCache* cache = [TMCache sharedCache];
[cache removeAllObjects];
[cache setObject:@"old" forKey:@"key"];
[cache.memoryCache removeAllObjects];
[cache objectForKey:@"key" block:^(TMCache* cache, NSString* key, id object) {
NSLog(@"get %@", object);
}];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[cache setObject:@"new" forKey:@"key" block:^(TMCache* cache, NSString* key, id object) {
NSLog(@"set %@", object);
NSLog(@"get2 %@", [cache objectForKey:@"key"]);
}];
});I expect that this issue manifests extremely rarely, but I figured it's worth pointing out anyway.