[iOS][4.5.94] PAGImageView setHidden: 经 Task::wait 重入 flush 导致主线程卡死(Fatal App Hang)

Aperta
#3,790 2 commenti 0 reazioni 1 assegnatario Vedi su GitHub

I maintainer di solito rispondono entro 1 giorno

@leiyue123 ci sta già lavorando.

Dal 26/10/2026.

Valutazione

Questa issue non è ancora stata valutata.

Descrizione

【版本信息】

  • libpag 4.5.94,iOS 原生,CocoaPods 集成。
  • 真实线上 Sentry 样本报告为 Fatal App Hang Fully Blocked,主线程阻塞至少 2000 ms;示例系统版本为 iOS 27.0.1。
  • 下文关于 4.5.98 / main 的内容仅为源码对照,尚未在 4.5.98 上完成同场景复现或验证。

【实际情况】

一个已经播放的 PAGImageView 在列表状态更新时从可见变为隐藏,主线程调用 setHidden:YES 后发生卡死。采样堆栈显示:setHidden: 经 PAGAnimator::setDuration、extractAndWaitTask、tgfx::Task::wait,在同一个主线程栈内进入了 flush,最终停在 imageViewLock 对应的 mutex 获取处。

资源由本地 Bundle 加载,并非网络 URL,因此这个调用点不涉及弱网下载。

【预期表现】

在主线程隐藏正在播放的 PAGImageView,不应触发持锁状态下的同步 flush 重入,也不应造成长期主线程阻塞。

【真实堆栈】

以下保留 Sentry 样本的原始帧顺序和 SDK 源文件行号;未符号化的系统帧不猜测函数名。业务帧、应用名称、路径、地址、用户/设备标识以及内部 Sentry 链接均已移除。

#00  [unsymbolicated system frame]
#01  [unsymbolicated system frame]
#02  [unsymbolicated system frame]
#03  [unsymbolicated system frame]
#04  std::__1::lock_guard<T>::lock_guard[abi:nqe210106]
     lock_guard.h:32
#05  std::__1::lock_guard<T>::lock_guard[abi:nqe210106]
     lock_guard.h:31
#06  -[PAGImageView flush]
     PAGImageView.mm:703
#07  pag::AnimatorListener::onAnimationUpdate
     PAGAnimator.mm:121
#08  pag::PAGAnimator::onFlush
     PAGAnimator.cpp:336
#09  pag::PAGAnimator::flushAsync::lambda::operator()
     PAGAnimator.cpp:247
#10  std::__1::__invoke[abi:nqe210106]<T>
     invoke.h:87
#11  std::__1::__invoke_void_return_wrapper<T>::__call[abi:nqe210106]<T>
     invoke.h:342
#12  std::__1::__invoke_r[abi:nqe210106]<T>
     invoke.h:348
#13  std::__1::__function::__func<T>::operator()
     function.h:174
#14  tgfx::Task::wait
     Task.cpp:80
#15  pag::PAGAnimator::extractAndWaitTask
     PAGAnimator.cpp:232
#16  pag::PAGAnimator::setDuration
     PAGAnimator.cpp:139
#17  -[PAGImageView setHidden:]
     PAGImageView.mm:501
... application and unrelated frames omitted ...

【源码推断,待维护者确认】

  1. PAGImageView::setHidden: 持有非递归 imageViewLock 后调用 checkVisible,隐藏时会调用 setDuration(0)。
  2. 4.5.94 的 extractAndWaitTask 调用 pendingTask->wait(500)。真实堆栈显示 wait 内部进入了动画 flush,而 PAGImageView::flush 会再次获取 imageViewLock。这与同线程重复获取非递归锁相符,但单次采样不能独立证明全部锁归属或唯一根因。
  3. #3407 已将 setBounds: / setFrame: 的 animator update 移到锁外;当前 setHidden: / setAlpha: 仍有持锁调用 checkVisible 的路径。
  4. #3554 为等待任务增加了 500 ms 超时,但如果 wait 内同步执行的任务本身进入阻塞,是否能受该超时保护,需要确认。
  5. #3730 / 4.5.98 已调整异步调度,并在 extractAndWaitTask 中增加 pending task cancellation 等处理。请确认该修改是否已经消除本 Issue 的确切重入链路。另一个需要核对的情形是:后台 flush 持有视图锁时,主线程直接等待 imageViewLock,该等待是否仍可能无界。

【希望确认】

  • 这条 setHidden -> setDuration -> Task::wait -> flush -> mutex 链路是否属于已知问题?
  • 4.5.98 的 #3730 是否已修复该精确链路,还是仍需要把 animator 操作移出 imageViewLock?
  • 对 setHidden: / setAlpha: 与后台 flush 竞争视图锁的情形,官方是否有推荐修复或可回移到 4.5.94 的补丁?

相关:#3407、#3554、#3730。这里报告的是 iOS 隐藏路径上的锁重入/等待问题,不是网络下载或解码器销毁 UAF。

【附件】

下图根据同一条真实 Sentry 事件的 SDK 堆栈脱敏重绘,便于阅读;不是 Sentry 页面原始截图。

Image
Lingua principale
HTML
Stelle
5.8k
Fork
533
Merge medio
3g 1h
PR unite (30g)
28

Preparare l'ambiente

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di Tencent/libpag

Tutte le issue di Tencent/libpag

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.