objectForKey:block: fails to call completion block when asked to process "large" numbers of requests
prendio2 opened this issue · comments
I noticed this issue when trying to cache a number of images at the same time in my own project and was able to recreate it in a sample project by simply requesting lots of object lookups in quick succession:
for (int i=1; i<=100; i++)
{
[[TMCache sharedCache] objectForKey:[NSString stringWithFormat:@"key %d",i]
block:^(TMCache *cache, NSString *key, id object) {
NSLog(@"completed %@",key);
}];
}
I would expect the above to output:
completed key 1
completed key 2
completed key 3
…
completed key 100
but it generally drops out after 1 or 2, or sometimes doesn't respond at all.
By switching:
_queue = dispatch_queue_create([queueName UTF8String], DISPATCH_QUEUE_CONCURRENT);
in TMCache.m:33 with:
_queue = dispatch_queue_create([queueName UTF8String], DISPATCH_QUEUE_PRIORITY_DEFAULT);
I was able to get the cache responding as expected but this may have other side effects that I am unaware of. Any thoughts?
Great catch, thank you!
What's happening is that GCD is hitting its thread limit (~= 64). Here's some brief discussion:
http://stackoverflow.com/questions/7213845/number-of-threads-created-by-gcd
Basically, TMCache
has been using TMMemoryCache
with a synchronous call to objectForKey:
, which should theoretically be fine because it's happening on another queue (that is, until it's waiting for more objects than there are threads and they all get blocked).
The fix was to switch everything in objectForKey:
to asynchronous calls, which is messier but ultimately safer and avoids this problem. I've also added a unit test to hopefully catch this if it happens in the future. Here's the commit: e44251f