Tencent / Tencent/libpag

PAGImageView 同步下载由于持有锁导致的 watchdog 问题

Open
#2,396 1 comment 0 reactions 1 assignee View on GitHub

@kevingpqi123 is already working on this.

Since Jul 31, 2024.

Dominant language
HTML
Stars
5.8k
Forks
531
Avg merge
8d 55m
Merged PRs (30d)
17

Description

【版本信息】

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及附件】

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.