Merge pull request #2999 from dreampiggy/bugfix_prefetcher_cancel_hung

Fix the issue that NSOperation conforms to `SDWebImageOperation` check failed. And fix the SDAsyncBlockOperation cancel logic
This commit is contained in:
DreamPiggy 2020-05-06 21:17:22 +08:00 committed by GitHub
commit 1fa07b7fd3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 108 additions and 42 deletions

View File

@ -112,6 +112,8 @@
327054D6206CD8B3006EA328 /* SDImageAPNGCoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 327054D2206CD8B3006EA328 /* SDImageAPNGCoder.h */; settings = {ATTRIBUTES = (Public, ); }; };
327054DA206CD8B3006EA328 /* SDImageAPNGCoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 327054D3206CD8B3006EA328 /* SDImageAPNGCoder.m */; };
327054DC206CD8B3006EA328 /* SDImageAPNGCoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 327054D3206CD8B3006EA328 /* SDImageAPNGCoder.m */; };
327F2E83245AE1650075F846 /* SDWebImageOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 327F2E82245AE1650075F846 /* SDWebImageOperation.m */; };
327F2E84245AE1650075F846 /* SDWebImageOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 327F2E82245AE1650075F846 /* SDWebImageOperation.m */; };
328BB69E2081FED200760D6C /* SDWebImageCacheKeyFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 328BB69A2081FED200760D6C /* SDWebImageCacheKeyFilter.h */; settings = {ATTRIBUTES = (Public, ); }; };
328BB6A22081FED200760D6C /* SDWebImageCacheKeyFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = 328BB69B2081FED200760D6C /* SDWebImageCacheKeyFilter.m */; };
328BB6A42081FED200760D6C /* SDWebImageCacheKeyFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = 328BB69B2081FED200760D6C /* SDWebImageCacheKeyFilter.m */; };
@ -437,6 +439,7 @@
326E2F32236F1D58006F847F /* SDDeviceHelper.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDDeviceHelper.m; sourceTree = "<group>"; };
327054D2206CD8B3006EA328 /* SDImageAPNGCoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDImageAPNGCoder.h; path = Core/SDImageAPNGCoder.h; sourceTree = "<group>"; };
327054D3206CD8B3006EA328 /* SDImageAPNGCoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDImageAPNGCoder.m; path = Core/SDImageAPNGCoder.m; sourceTree = "<group>"; };
327F2E82245AE1650075F846 /* SDWebImageOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDWebImageOperation.m; path = Core/SDWebImageOperation.m; sourceTree = "<group>"; };
328BB69A2081FED200760D6C /* SDWebImageCacheKeyFilter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDWebImageCacheKeyFilter.h; path = Core/SDWebImageCacheKeyFilter.h; sourceTree = "<group>"; };
328BB69B2081FED200760D6C /* SDWebImageCacheKeyFilter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDWebImageCacheKeyFilter.m; path = Core/SDWebImageCacheKeyFilter.m; sourceTree = "<group>"; };
328BB6A82081FEE500760D6C /* SDWebImageCacheSerializer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDWebImageCacheSerializer.h; path = Core/SDWebImageCacheSerializer.h; sourceTree = "<group>"; };
@ -846,6 +849,7 @@
320CAE132086F50500CFFC80 /* SDWebImageError.h */,
320CAE142086F50500CFFC80 /* SDWebImageError.m */,
530E49E71646388E002868E7 /* SDWebImageOperation.h */,
327F2E82245AE1650075F846 /* SDWebImageOperation.m */,
324DF4B2200A14DC008A84CC /* SDWebImageDefine.h */,
324DF4B3200A14DC008A84CC /* SDWebImageDefine.m */,
325312C6200F09910046BF1E /* SDWebImageTransition.h */,
@ -1196,6 +1200,7 @@
4A2CAE201AB4BB6C00B6BC39 /* SDImageCache.m in Sources */,
4369C2801D9807EC007E863A /* UIView+WebCache.m in Sources */,
329A18611FFF5DFD008C9A2F /* UIImage+Metadata.m in Sources */,
327F2E84245AE1650075F846 /* SDWebImageOperation.m in Sources */,
328BB6B22081FEE500760D6C /* SDWebImageCacheSerializer.m in Sources */,
325C4611223394D8004CAE11 /* SDImageCachesManagerOperation.m in Sources */,
);
@ -1270,6 +1275,7 @@
ABBE71A818C43B4D00B75E91 /* UIImageView+HighlightedWebCache.m in Sources */,
4369C27E1D9807EC007E863A /* UIView+WebCache.m in Sources */,
329A185F1FFF5DFD008C9A2F /* UIImage+Metadata.m in Sources */,
327F2E83245AE1650075F846 /* SDWebImageOperation.m in Sources */,
328BB6B02081FEE500760D6C /* SDWebImageCacheSerializer.m in Sources */,
325C4610223394D8004CAE11 /* SDImageCachesManagerOperation.m in Sources */,
);

View File

@ -0,0 +1,14 @@
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey <rs@dailymotion.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#import "SDWebImageOperation.h"
/// NSOperation conform to `SDWebImageOperation`.
@implementation NSOperation (SDWebImageOperation)
@end

View File

@ -20,6 +20,10 @@
atomic_flag _isAllFinished;
unsigned long _totalCount;
// Used to ensure NSPointerArray thread safe
dispatch_semaphore_t _prefetchOperationsLock;
dispatch_semaphore_t _loadOperationsLock;
}
@property (nonatomic, copy, readwrite) NSArray<NSURL *> *urls;
@ -106,7 +110,6 @@
}
- (void)startPrefetchWithToken:(SDWebImagePrefetchToken * _Nonnull)token {
NSPointerArray *operations = token.loadOperations;
for (NSURL *url in token.urls) {
@autoreleasepool {
@weakify(self);
@ -142,13 +145,13 @@
[asyncOperation complete];
}];
NSAssert(operation != nil, @"Operation should not be nil, [SDWebImageManager loadImageWithURL:options:context:progress:completed:] break prefetch logic");
@synchronized (token) {
[operations addPointer:(__bridge void *)operation];
}
SD_LOCK(token->_loadOperationsLock);
[token.loadOperations addPointer:(__bridge void *)operation];
SD_UNLOCK(token->_loadOperationsLock);
}];
@synchronized (token) {
SD_LOCK(token->_prefetchOperationsLock);
[token.prefetchOperations addPointer:(__bridge void *)prefetchOperation];
}
SD_UNLOCK(token->_prefetchOperationsLock);
[self.prefetchQueue addOperation:prefetchOperation];
}
}
@ -262,24 +265,38 @@
@implementation SDWebImagePrefetchToken
- (instancetype)init {
self = [super init];
if (self) {
_prefetchOperationsLock = dispatch_semaphore_create(1);
_loadOperationsLock = dispatch_semaphore_create(1);
}
return self;
}
- (void)cancel {
@synchronized (self) {
SD_LOCK(_prefetchOperationsLock);
[self.prefetchOperations compact];
for (id operation in self.prefetchOperations) {
if ([operation conformsToProtocol:@protocol(SDWebImageOperation)]) {
[operation cancel];
id<SDWebImageOperation> strongOperation = operation;
if (strongOperation) {
[strongOperation cancel];
}
}
self.prefetchOperations.count = 0;
SD_UNLOCK(_prefetchOperationsLock);
SD_LOCK(_loadOperationsLock);
[self.loadOperations compact];
for (id operation in self.loadOperations) {
if ([operation conformsToProtocol:@protocol(SDWebImageOperation)]) {
[operation cancel];
id<SDWebImageOperation> strongOperation = operation;
if (strongOperation) {
[strongOperation cancel];
}
}
self.loadOperations.count = 0;
}
SD_UNLOCK(_loadOperationsLock);
self.completionBlock = nil;
self.progressBlock = nil;
[self.prefetcher removeRunningToken:self];

View File

@ -35,33 +35,58 @@
}
- (void)start {
@synchronized (self) {
if (self.isCancelled) {
self.finished = YES;
return;
}
[self willChangeValueForKey:@"isExecuting"];
self.finished = NO;
self.executing = YES;
[self didChangeValueForKey:@"isExecuting"];
if (self.executionBlock) {
self.executionBlock(self);
} else {
[self complete];
self.executing = NO;
self.finished = YES;
}
}
}
- (void)cancel {
@synchronized (self) {
[super cancel];
[self complete];
}
- (void)complete {
[self willChangeValueForKey:@"isExecuting"];
[self willChangeValueForKey:@"isFinished"];
if (self.isExecuting) {
self.executing = NO;
self.finished = YES;
[self didChangeValueForKey:@"isExecuting"];
}
}
}
- (void)complete {
@synchronized (self) {
if (self.isExecuting) {
self.finished = YES;
self.executing = NO;
}
}
}
- (void)setFinished:(BOOL)finished {
[self willChangeValueForKey:@"isFinished"];
_finished = finished;
[self didChangeValueForKey:@"isFinished"];
}
- (void)setExecuting:(BOOL)executing {
[self willChangeValueForKey:@"isExecuting"];
_executing = executing;
[self didChangeValueForKey:@"isExecuting"];
}
- (BOOL)isConcurrent {
return YES;
}
@end

View File

@ -95,6 +95,10 @@
operation = token.downloadOperation;
expect([operation class]).to.equal([SDWebImageTestDownloadOperation class]);
// Assert the NSOperation conforms to `SDWebImageOperation`
expect([NSOperation.class conformsToProtocol:@protocol(SDWebImageOperation)]).beTruthy();
expect([operation conformsToProtocol:@protocol(SDWebImageOperation)]).beTruthy();
// back to the original value
downloader.config.operationClass = nil;
token = [downloader downloadImageWithURL:imageURL3 options:0 progress:nil completed:nil];