Skip to content

[Android] Close FlutterRenderer texture image SyncFence objects immediately after waiting on them#188313

Merged
auto-submit[bot] merged 1 commit into
flutter:masterfrom
jason-simmons:bug_188161
Jun 24, 2026
Merged

[Android] Close FlutterRenderer texture image SyncFence objects immediately after waiting on them#188313
auto-submit[bot] merged 1 commit into
flutter:masterfrom
jason-simmons:bug_188161

Conversation

@jason-simmons

Copy link
Copy Markdown
Member

Each SyncFence may use a file descriptor. But the SyncFences were not being closed promptly and their file descriptors were not being deleted until the SyncFences were garbage collected.

If the process is rendering video and creating a SyncFence for each frame, then it may run out of file descriptor capacity before Java GC happens.

Fixes #188161

@jason-simmons
jason-simmons requested a review from mboetger June 22, 2026 15:08
@jason-simmons
jason-simmons requested a review from a team as a code owner June 22, 2026 15:08
@flutter-dashboard flutter-dashboard Bot added the CICD Run CI/CD label Jun 22, 2026
@github-actions github-actions Bot added platform-android Android applications specifically engine flutter/engine related. See also e: labels. team-android Owned by Android platform team labels Jun 22, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors FlutterRenderer.java to use try-with-resources for SyncFence in waitOnFence to ensure it is closed, and adds a unit test to verify this behavior. The reviewer suggests refactoring the duplicated waitOnFence method from two inner classes into a single helper method in the outer class to improve maintainability.

Comment on lines 1067 to 1072
@RequiresApi(API_LEVELS.API_33)
private void waitOnFence(Image image) {
try {
SyncFence fence = image.getFence();
try (SyncFence fence = image.getFence()) {
fence.awaitForever();
} catch (IOException e) {
// Drop.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

There is an identical implementation of waitOnFence(Image) at line 789. Since both containing classes are inner classes of FlutterRenderer, you can eliminate this duplication by moving waitOnFence to the outer FlutterRenderer class as a single package-private or private static helper method, and calling it from both inner classes.

References
  1. Suggest simplification and refactoring to enhance readability and maintainability, and avoid duplicating code/state where possible. (link)

…iately after waiting on them

Each SyncFence may use a file descriptor.  But the SyncFences were not
being closed promptly and their file descriptors were not being deleted
until the SyncFences were garbage collected.

If the process is rendering video and creating a SyncFence for each
frame, then it may run out of file descriptor capacity before Java GC
happens.

Fixes flutter#188161
@github-actions github-actions Bot removed the CICD Run CI/CD label Jun 23, 2026
@jason-simmons jason-simmons added the CICD Run CI/CD label Jun 23, 2026
@jesswrd jesswrd added the autosubmit Merge PR when tree becomes green via auto submit App label Jun 23, 2026
@auto-submit
auto-submit Bot added this pull request to the merge queue Jun 23, 2026
Merged via the queue into flutter:master with commit 909a795 Jun 24, 2026
205 checks passed
@flutter-dashboard flutter-dashboard Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Jun 24, 2026
via-guy pushed a commit to via-guy/flutter that referenced this pull request Jun 26, 2026
…iately after waiting on them (flutter#188313)

Each SyncFence may use a file descriptor. But the SyncFences were not
being closed promptly and their file descriptors were not being deleted
until the SyncFences were garbage collected.

If the process is rendering video and creating a SyncFence for each
frame, then it may run out of file descriptor capacity before Java GC
happens.

Fixes flutter#188161
@JonasJW

JonasJW commented Jul 10, 2026

Copy link
Copy Markdown

Requesting a cherry-pick of this fix to stable (3.44) — and, since the
current 3.46 candidate branch appears to have been cut before this merge,
possibly beta as well. Per the cherry-pick process, providing the required
fields below in the hope a team member can apply the cp: label(s).

Impacted Users: Any app rendering sustained SurfaceProducer-backed
textures on Android 13+ — this includes the stock video_player plugin
(video_player_android 2.9.5), not only flutter_webrtc. End customers of
3.44-built apps.

Impact Description: One sync_file fd leaks per rendered frame; the process
hits the 32768 RLIMIT_NOFILE ceiling mid-session. Fence dup/merge then fails
with EMFILE, the raster thread blocks forever in Surface::dequeueBuffer, and
the app freezes permanently (silent freeze, or "Input dispatching timed out"
ANR in flutter::SurfaceChanged if the user touches the screen).

Production data from our fitness app (30-60 min workout sessions with looping
exercise videos + animated WebP thumbnails):

Workaround: None in-app; the leak is in the engine's Java embedding. We
downgraded to 3.41.x and disabled animated thumbnails on Android.

Risk: Low — the fix closes an AutoCloseable (SyncFence) immediately
after awaitForever() via try-with-resources; small, local, merged on master
since 2026-06-24, with a regression test asserting close() is called.

Test Coverage: Reproduction in #188161 (flutter_webrtc example, Pixel 6a);
regression test in this PR.

Validation Steps: Run any long video_player session on an Android 13+
device and watch ls /proc/<pid>/fd | wc -l — flat with the fix, climbs
per-frame without it. We additionally ship fd-count telemetry in production
and can verify a hotfix build against a fleet that reproduces this weekly.

@jason-simmons jason-simmons added the cp: stable cherry pick this pull request to stable release candidate branch label Jul 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CICD Run CI/CD cp: stable cherry pick this pull request to stable release candidate branch engine flutter/engine related. See also e: labels. platform-android Android applications specifically team-android Owned by Android platform team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Android] ImageReaderSurfaceProducer.waitOnFence leaks one sync_file FD per frame (image.getFence() never closed) → freeze at FD limit (API 33+)

4 participants