Fix the issue that "There may be no complete callback when download the picture of the local path"

This commit is contained in:
DreamPiggy 2019-12-26 17:38:46 +08:00
parent 6ea9a7640a
commit 482d45dc65
1 changed files with 17 additions and 10 deletions

View File

@ -293,6 +293,7 @@
} }
LOCK(self.operationsLock); LOCK(self.operationsLock);
id downloadOperationCancelToken;
NSOperation<SDWebImageDownloaderOperationInterface> *operation = [self.URLOperations objectForKey:url]; NSOperation<SDWebImageDownloaderOperationInterface> *operation = [self.URLOperations objectForKey:url];
// There is a case that the operation may be marked as finished or cancelled, but not been removed from `self.URLOperations`. // There is a case that the operation may be marked as finished or cancelled, but not been removed from `self.URLOperations`.
if (!operation || operation.isFinished || operation.isCancelled) { if (!operation || operation.isFinished || operation.isCancelled) {
@ -308,22 +309,28 @@
UNLOCK(sself.operationsLock); UNLOCK(sself.operationsLock);
}; };
[self.URLOperations setObject:operation forKey:url]; [self.URLOperations setObject:operation forKey:url];
// Add the handlers before submitting to operation queue, avoid the race condition that operation finished before setting handlers.
downloadOperationCancelToken = [operation addHandlersForProgress:progressBlock completed:completedBlock];
// Add operation to operation queue only after all configuration done according to Apple's doc. // Add operation to operation queue only after all configuration done according to Apple's doc.
// `addOperation:` does not synchronously execute the `operation.completionBlock` so this will not cause deadlock. // `addOperation:` does not synchronously execute the `operation.completionBlock` so this will not cause deadlock.
[self.downloadQueue addOperation:operation]; [self.downloadQueue addOperation:operation];
} } else {
else if (!operation.isExecuting) { // When we reuse the download operation to attach more callbacks, there may be thread safe issue because the getter of callbacks may in another queue (decoding queue or delegate queue)
if (options & SDWebImageDownloaderHighPriority) { // So we lock the operation here, and in `SDWebImageDownloaderOperation`, we use `@synchonzied (self)`, to ensure the thread safe between these two classes.
operation.queuePriority = NSOperationQueuePriorityHigh; @synchronized (operation) {
} else if (options & SDWebImageDownloaderLowPriority) { downloadOperationCancelToken = [operation addHandlersForProgress:progressBlock completed:completedBlock];
operation.queuePriority = NSOperationQueuePriorityLow; }
} else { if (!operation.isExecuting) {
operation.queuePriority = NSOperationQueuePriorityNormal; if (options & SDWebImageDownloaderHighPriority) {
operation.queuePriority = NSOperationQueuePriorityHigh;
} else if (options & SDWebImageDownloaderLowPriority) {
operation.queuePriority = NSOperationQueuePriorityLow;
} else {
operation.queuePriority = NSOperationQueuePriorityNormal;
}
} }
} }
UNLOCK(self.operationsLock); UNLOCK(self.operationsLock);
id downloadOperationCancelToken = [operation addHandlersForProgress:progressBlock completed:completedBlock];
SDWebImageDownloadToken *token = [SDWebImageDownloadToken new]; SDWebImageDownloadToken *token = [SDWebImageDownloadToken new];
token.downloadOperation = operation; token.downloadOperation = operation;