|
@@ -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 if (!operation.isExecuting) {
|
|
|
|
|
- if (options & SDWebImageDownloaderHighPriority) {
|
|
|
|
|
- operation.queuePriority = NSOperationQueuePriorityHigh;
|
|
|
|
|
- } else if (options & SDWebImageDownloaderLowPriority) {
|
|
|
|
|
- operation.queuePriority = NSOperationQueuePriorityLow;
|
|
|
|
|
- } else {
|
|
|
|
|
- operation.queuePriority = NSOperationQueuePriorityNormal;
|
|
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 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)
|
|
|
|
|
+ // So we lock the operation here, and in `SDWebImageDownloaderOperation`, we use `@synchonzied (self)`, to ensure the thread safe between these two classes.
|
|
|
|
|
+ @synchronized (operation) {
|
|
|
|
|
+ downloadOperationCancelToken = [operation addHandlersForProgress:progressBlock completed:completedBlock];
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!operation.isExecuting) {
|
|
|
|
|
+ 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;
|