Add back some required locking

This commit is contained in:
Matt Galloway 2014-09-15 11:41:16 +01:00
parent ae91053a80
commit f773870d2c
2 changed files with 66 additions and 37 deletions

View File

@ -169,12 +169,14 @@
return;
}
_SDWebImageDownloaderToken *typedToken = (_SDWebImageDownloaderToken *)token;
SDWebImageDownloaderOperation *operation = self.URLOperations[typedToken.url];
BOOL canceled = [operation cancel:typedToken.downloadOperationCancelToken];
if (canceled) {
[self.URLOperations removeObjectForKey:typedToken.url];
}
dispatch_barrier_async(self.barrierQueue, ^{
_SDWebImageDownloaderToken *typedToken = (_SDWebImageDownloaderToken *)token;
SDWebImageDownloaderOperation *operation = self.URLOperations[typedToken.url];
BOOL canceled = [operation cancel:typedToken.downloadOperationCancelToken];
if (canceled) {
[self.URLOperations removeObjectForKey:typedToken.url];
}
});
}
- (id)addProgressCallback:(SDWebImageDownloaderProgressBlock)progressBlock completedBlock:(SDWebImageDownloaderCompletedBlock)completedBlock forURL:(NSURL *)url createCallback:(SDWebImageDownloaderOperation *(^)())createCallback {
@ -186,25 +188,29 @@
return nil;
}
SDWebImageDownloaderOperation *operation = self.URLOperations[url];
if (!operation) {
operation = createCallback();
self.URLOperations[url] = operation;
__block _SDWebImageDownloaderToken *token = nil;
__weak SDWebImageDownloaderOperation *woperation = operation;
operation.completionBlock = ^{
SDWebImageDownloaderOperation *soperation = woperation;
if (!soperation) return;
if (self.URLOperations[url] == soperation) {
[self.URLOperations removeObjectForKey:url];
};
};
}
id downloadOperationCancelToken = [operation addHandlersForProgress:progressBlock completed:completedBlock];
dispatch_barrier_sync(self.barrierQueue, ^{
SDWebImageDownloaderOperation *operation = self.URLOperations[url];
if (!operation) {
operation = createCallback();
self.URLOperations[url] = operation;
_SDWebImageDownloaderToken *token = [_SDWebImageDownloaderToken new];
token.url = url;
token.downloadOperationCancelToken = downloadOperationCancelToken;
__weak SDWebImageDownloaderOperation *woperation = operation;
operation.completionBlock = ^{
SDWebImageDownloaderOperation *soperation = woperation;
if (!soperation) return;
if (self.URLOperations[url] == soperation) {
[self.URLOperations removeObjectForKey:url];
};
};
}
id downloadOperationCancelToken = [operation addHandlersForProgress:progressBlock completed:completedBlock];
token = [_SDWebImageDownloaderToken new];
token.url = url;
token.downloadOperationCancelToken = downloadOperationCancelToken;
});
return token;
}

View File

@ -29,6 +29,7 @@ static NSString *const kCompletedCallbackKey = @"completed";
@property (strong, nonatomic) NSMutableData *imageData;
@property (strong, nonatomic) NSURLConnection *connection;
@property (strong, atomic) NSThread *thread;
@property (SDDispatchQueueSetterSementics, nonatomic) dispatch_queue_t barrierQueue;
#if TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0
@property (assign, nonatomic) UIBackgroundTaskIdentifier backgroundTaskId;
@ -57,26 +58,46 @@ static NSString *const kCompletedCallbackKey = @"completed";
_finished = NO;
_expectedSize = 0;
responseFromCached = YES; // Initially wrong until `connection:willCacheResponse:` is called or not called
_barrierQueue = dispatch_queue_create("com.hackemist.SDWebImageDownloaderOperationBarrierQueue", DISPATCH_QUEUE_CONCURRENT);
}
return self;
}
- (void)dealloc {
SDDispatchQueueRelease(_barrierQueue);
}
- (id)addHandlersForProgress:(SDWebImageDownloaderProgressBlock)progressBlock
completed:(SDWebImageDownloaderCompletedBlock)completedBlock {
NSMutableDictionary *callbacks = [NSMutableDictionary new];
if (progressBlock) callbacks[kProgressCallbackKey] = [progressBlock copy];
if (completedBlock) callbacks[kCompletedCallbackKey] = [completedBlock copy];
[self.callbackBlocks addObject:callbacks];
dispatch_barrier_async(self.barrierQueue, ^{
[self.callbackBlocks addObject:callbacks];
});
return callbacks;
}
- (NSArray *)callbacksForKey:(NSString *)key {
__block NSArray *callbacks = nil;
dispatch_sync(self.barrierQueue, ^{
callbacks = [self.callbackBlocks valueForKey:key];
});
return callbacks;
}
- (BOOL)cancel:(id)token {
[self.callbackBlocks removeObjectIdenticalTo:token];
if (self.callbackBlocks.count == 0) {
__block BOOL shouldCancel = NO;
dispatch_barrier_sync(self.barrierQueue, ^{
[self.callbackBlocks removeObjectIdenticalTo:token];
if (self.callbackBlocks.count == 0) {
shouldCancel = YES;
}
});
if (shouldCancel) {
[self cancel];
return YES;
}
return NO;
return shouldCancel;
}
- (void)start {
@ -114,7 +135,7 @@ static NSString *const kCompletedCallbackKey = @"completed";
[self.connection start];
if (self.connection) {
for (SDWebImageDownloaderProgressBlock progressBlock in [self.callbackBlocks valueForKey:kProgressCallbackKey]) {
for (SDWebImageDownloaderProgressBlock progressBlock in [self callbacksForKey:kProgressCallbackKey]) {
progressBlock(0, NSURLResponseUnknownLength);
}
dispatch_async(dispatch_get_main_queue(), ^{
@ -137,7 +158,7 @@ static NSString *const kCompletedCallbackKey = @"completed";
}
}
else {
for (SDWebImageDownloaderCompletedBlock completedBlock in [self.callbackBlocks valueForKey:kCompletedCallbackKey]) {
for (SDWebImageDownloaderCompletedBlock completedBlock in [self callbacksForKey:kCompletedCallbackKey]) {
completedBlock(nil, nil, [NSError errorWithDomain:NSURLErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Connection can't be initialized"}], YES);
}
}
@ -198,7 +219,9 @@ static NSString *const kCompletedCallbackKey = @"completed";
}
- (void)reset {
[self.callbackBlocks removeAllObjects];
dispatch_barrier_async(self.barrierQueue, ^{
[self.callbackBlocks removeAllObjects];
});
self.connection = nil;
self.imageData = nil;
self.thread = nil;
@ -228,7 +251,7 @@ static NSString *const kCompletedCallbackKey = @"completed";
if (![response respondsToSelector:@selector(statusCode)] || ([((NSHTTPURLResponse *)response) statusCode] < 400 && [((NSHTTPURLResponse *)response) statusCode] != 304)) {
NSInteger expected = response.expectedContentLength > 0 ? (NSInteger)response.expectedContentLength : 0;
self.expectedSize = expected;
for (SDWebImageDownloaderProgressBlock progressBlock in [self.callbackBlocks valueForKey:kProgressCallbackKey]) {
for (SDWebImageDownloaderProgressBlock progressBlock in [self callbacksForKey:kProgressCallbackKey]) {
progressBlock(0, expected);
}
@ -252,7 +275,7 @@ static NSString *const kCompletedCallbackKey = @"completed";
[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:self];
});
for (SDWebImageDownloaderCompletedBlock completedBlock in [self.callbackBlocks valueForKey:kCompletedCallbackKey]) {
for (SDWebImageDownloaderCompletedBlock completedBlock in [self callbacksForKey:kCompletedCallbackKey]) {
completedBlock(nil, nil, [NSError errorWithDomain:NSURLErrorDomain code:[((NSHTTPURLResponse *)response) statusCode] userInfo:nil], YES);
}
CFRunLoopStop(CFRunLoopGetCurrent());
@ -330,7 +353,7 @@ static NSString *const kCompletedCallbackKey = @"completed";
}
CGImageRelease(partialImageRef);
dispatch_main_sync_safe(^{
for (SDWebImageDownloaderCompletedBlock completedBlock in [self.callbackBlocks valueForKey:kCompletedCallbackKey]) {
for (SDWebImageDownloaderCompletedBlock completedBlock in [self callbacksForKey:kCompletedCallbackKey]) {
completedBlock(image, nil, nil, NO);
}
});
@ -340,7 +363,7 @@ static NSString *const kCompletedCallbackKey = @"completed";
CFRelease(imageSource);
}
for (SDWebImageDownloaderProgressBlock progressBlock in [self.callbackBlocks valueForKey:kProgressCallbackKey]) {
for (SDWebImageDownloaderProgressBlock progressBlock in [self callbacksForKey:kProgressCallbackKey]) {
progressBlock(self.imageData.length, self.expectedSize);
}
}
@ -373,7 +396,7 @@ static NSString *const kCompletedCallbackKey = @"completed";
}
- (void)connectionDidFinishLoading:(NSURLConnection *)aConnection {
NSArray *completionBlocks = [[self.callbackBlocks valueForKey:kCompletedCallbackKey] copy];
NSArray *completionBlocks = [[self callbacksForKey:kCompletedCallbackKey] copy];
@synchronized(self) {
CFRunLoopStop(CFRunLoopGetCurrent());
self.thread = nil;
@ -431,7 +454,7 @@ static NSString *const kCompletedCallbackKey = @"completed";
});
}
for (SDWebImageDownloaderCompletedBlock completedBlock in [self.callbackBlocks valueForKey:kCompletedCallbackKey]) {
for (SDWebImageDownloaderCompletedBlock completedBlock in [self callbacksForKey:kCompletedCallbackKey]) {
completedBlock(nil, nil, error, YES);
}
self.completionBlock = nil;