libpag
libpag copied to clipboard
PAGImageView 同步下载由于持有锁导致的 watchdog 问题
【版本信息】
4.3.57
【平台信息】
iOS 原生
【预期的表现】
子线程 PAGImageView 触发 flush 方法进行下载,主线程调用 PAGImageView 的 setHidden: 方法,不应该触发 watchdog
【实际的情况】
子线程 PAGImageView 触发 flush 方法会导致 PAG 文件的下载,最终会调用到下面的方法进行同步下载:
+ (PAGFile*)Load:(NSString*)path {
if (path == nil) {
return nil;
}
if ([PAGFileImpl IsNetWorkPath:path]) {
NSData* cacheData = [PAGDiskCacheImpl ReadFile:path];
if (cacheData == nil) {
NSError* error = nil;
cacheData = [NSData dataWithContentsOfURL:[NSURL URLWithString:path]
options:NSDataReadingUncached
error:&error]; // 这里进行同步的网络下载
if (error == nil && cacheData != nil) {
[PAGDiskCacheImpl WritFile:path data:cacheData];
}
}
return [PAGFileImpl Load:cacheData.bytes size:cacheData.length path:path];
}
auto pagFile = pag::PAGFile::Load([path UTF8String]);
if (pagFile == nullptr) {
return nil;
}
return (PAGFile*)[PAGLayerImpl ToPAGLayer:pagFile];
}
如果网络下载比较耗时,PAGImageView 的 flush 方法一开始就持有了一个锁:
- (BOOL)flush {
std::lock_guard<std::mutex> autoLock(imageViewLock); // 持有锁
NSInteger frameIndex = [self currentFrame];
if (self.memeoryCacheFinished) {
if ([self checkPAGCompositionChanged] == NO) {
if (self.currentFrameIndex != frameIndex) {
UIImage* image = imagesMap[@(frameIndex)];
if (image) {
self.currentFrameIndex = frameIndex;
self.currentUIImage = image;
[self submitToImageView];
return YES;
}
}
}
}
if (self.currentFrameIndex == frameIndex) {
return NO;
}
[self checkPAGCompositionChanged];
CVPixelBufferRef pixelBuffer = self.memoryCacheEnabled ? [self getMemoryCacheCVPixelBuffer]
: [self getDiskCacheCVPixelBuffer];
if (pixelBuffer == nil) {
self.currentUIImage = nil;
[self submitToImageView];
return NO;
}
return [self updateImageViewFrom:pixelBuffer atIndex:frameIndex];
}
如果网络下载一直不返回,PAGImageView 的 flush 会一直持有这个锁,这样,如果主线程调用了其他方法,比如 setHidden:就会阻塞在这个锁上:
- (void)setHidden:(BOOL)hidden {
[super setHidden:hidden];
std::lock_guard<std::mutex> autoLock(imageViewLock); // 主线程被阻塞在这个锁上
[self checkVisible];
}
如果主线程阻塞时间过长,就会导致 watchdog,应用被杀死
【Demo及附件】
这个问题,我也遇到了,一直不知道怎么解决,大量的堵塞,导致ANR, 我们的产品在海外,网络太差