Skip to content

Fix PAGImageView deadlock when size updates trigger flush#3348

Open
Batxent wants to merge 3 commits intoTencent:mainfrom
Batxent:fix/pagimageview-deadlock
Open

Fix PAGImageView deadlock when size updates trigger flush#3348
Batxent wants to merge 3 commits intoTencent:mainfrom
Batxent:fix/pagimageview-deadlock

Conversation

@Batxent
Copy link
Copy Markdown

@Batxent Batxent commented Mar 30, 2026

Summary

  • move PAGImageView animator updates out of the imageViewLock scope in setBounds: and setFrame:
  • prevent update -> onAnimationFlush -> flush() from re-entering the same non-recursive mutex
  • add inline comments to document the lock-ordering requirement

Root Cause

PAGImageView used to call [animator update] while still holding imageViewLock during size changes.

That update path can synchronously flow through:

update -> onAnimationFlush -> flush()

flush() also acquires imageViewLock, so the same thread can try to lock the same non-recursive mutex twice and deadlock.

Fix

Keep decoder reset and size-related state updates inside imageViewLock, but defer [animator update] until after the lock has been released.

This preserves the existing flush behavior while removing the re-entrant lock path.

Validation

  • reproduced a deadlock when PAGImageView handled size changes and an immediate flush was triggered
  • built libpag for iphoneos with the patch
  • verified the deadlock no longer occurs with the patched binary

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Mar 30, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 75.59%. Comparing base (f456817) to head (e8d31bd).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3348      +/-   ##
==========================================
- Coverage   75.60%   75.59%   -0.01%     
==========================================
  Files         510      510              
  Lines       35435    35435              
  Branches    11283    11283              
==========================================
- Hits        26789    26786       -3     
+ Misses       6356     6355       -1     
- Partials     2290     2294       +4     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@Batxent
Copy link
Copy Markdown
Author

Batxent commented Mar 30, 2026

The main issue here is lock re-entrancy.

setBounds: / setFrame: previously called [animator update] while imageViewLock was still held.
That update path can synchronously flow back into PAGImageView::flush(), which also acquires imageViewLock.

So the effective path was:

setBounds/setFrame
-> lock imageViewLock
-> update
-> onAnimationFlush
-> flush
-> lock imageViewLock again

Because the mutex is non-recursive, this can deadlock on the same thread.

The fix is to keep the decoder reset under the lock, but defer [animator update] until after unlock.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants