Skip to content

ADFA-2794 Another attempt to fix all the TimeoutException / unknown finalizer crashes#1239

Merged
hal-eisen-adfa merged 2 commits into
stagefrom
ADFA-2974-another-TimeoutException-attempt
Apr 24, 2026
Merged

ADFA-2794 Another attempt to fix all the TimeoutException / unknown finalizer crashes#1239
hal-eisen-adfa merged 2 commits into
stagefrom
ADFA-2974-another-TimeoutException-attempt

Conversation

@hal-eisen-adfa
Copy link
Copy Markdown
Collaborator

@hal-eisen-adfa hal-eisen-adfa commented Apr 24, 2026

Regarding APPDEVFORALL-E8 in Sentry...

  1. ZipFileSystem.java — Neutered finalize() to a no-op. This was the most likely culprit: its close() does heavy I/O (sync, channel close, inflater/deflater cleanup) that can exceed the 10s watchdog timeout on slow storage.
  2. ToolTipManager.kt — Rewrote cursor handling to use .use {} blocks. Previously, 3 cursors shared one variable — the first two were overwritten without closing, left for GC with their finalize() methods.
  3. VectorMasterView.java + VectorMasterDrawable.java — Added finally blocks to close vectorStream (FileInputStream) after XML parsing. FileInputStream.finalize() calls close() and is a known source of finalizer pressure on Android.
  4. StringFragment.java + ColorFragment.java + ValuesManager.java — Wrapped FileInputStream in try-with-resources. Same leak pattern.

Bonus!

  1. IDEApplication.kt — Added isFinalizerWatchdogTimeout() predicate that detects TimeoutException from the FinalizerWatchdogDaemon thread. When matched, it logs a warning, reports to Sentry as non-fatal, and returns without crashing. This catches any remaining finalizer timeouts from framework/third-party code we don't control. Other organizations do this quite commonly.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 24, 2026

📝 Walkthrough

Walkthrough

This PR centralizes resource cleanup and finalizer handling: it suppresses FinalizerWatchdogDaemon TimeoutExceptions in the uncaught-exception handler, removes I/O from a finalizer, converts several streams/cursors to scoped resource usage, and ensures vector streams are closed in finally blocks.

Changes

Cohort / File(s) Summary
Finalizer / uncaught-exception
app/src/main/java/com/itsaky/androidide/app/IDEApplication.kt
Detects TimeoutException occurrences tied to FinalizerWatchdogDaemon, logs/report to Sentry as suppressed (non-fatal) and prevents propagation.
ZipFileSystem finalizer
composite-builds/build-deps/java-compiler/src/main/java/com/itsaky/androidide/zipfs2/ZipFileSystem.java
Replaces previous finalizer that performed I/O/close with a no-op finalize() to avoid doing I/O during GC finalization.
Database / cursor scoped use
idetooltips/src/main/java/com/itsaky/androidide/idetooltips/ToolTipManager.kt
Replaces mutable cursor/db lifecycle with use-scoped blocks to ensure deterministic cursor and DB closure across queries.
File stream try-with-resources
layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/fragments/resources/ColorFragment.java, layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/fragments/resources/StringFragment.java, layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/managers/ValuesManager.java
Opens FileInputStream in try-with-resources (automatic close); broadens/adjusts IOException handling where applicable.
Vector stream finally cleanup
vectormaster/src/main/java/org/appdevforall/codeonthego/vectormaster/VectorMasterDrawable.java, vectormaster/src/main/java/org/appdevforall/codeonthego/vectormaster/VectorMasterView.java
Ensures vectorStream is closed in a finally block (ignores close failures) and nulls the reference to prevent reuse.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Possibly related PRs

Suggested reviewers

  • itsaky-adfa
  • Daniel-ADFA
  • jomen-adfa

Poem

🐰 I nibble at leaks and tidy each stream,
Hopping through code like a bright little beam.
Finalizers quiet, no crashes to chase,
Closed files and calm—what a peaceful place! 🎋

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main objective of fixing TimeoutException and finalizer-related crashes across multiple files in the codebase.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The pull request description clearly relates to the changeset, providing detailed context for each modified file and addressing finalizer-related crashes and resource management improvements.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ADFA-2974-another-TimeoutException-attempt

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
vectormaster/src/main/java/org/appdevforall/codeonthego/vectormaster/VectorMasterView.java (1)

99-117: ⚠️ Potential issue | 🟡 Minor

Same early-return leak: vectorStream not closed on parser-setup failure.

Mirror of the issue in VectorMasterDrawable.buildVectorModel(). If XmlPullParserException is thrown at Line 104 during xpp.setInput(...), the method returns at Line 109 before reaching the try at Line 125, so the new finally at Line 613 never runs and the FileInputStream opened at Line 92 remains open until finalization — which is the scenario this PR aims to eliminate.

🛠️ Close the stream on the early-return path
     XmlPullParser xpp;
     if (vectorStream != null) {
       try {
         XmlPullParserFactory parserFactory = XmlPullParserFactory.newInstance();
         parserFactory.setNamespaceAware(true);
         xpp = parserFactory.newPullParser();
         xpp.setInput(vectorStream, "utf-8");
       } catch (XmlPullParserException xml) {
         Log.e(TAG, "Error from reading vector image");
         xml.printStackTrace();
         vectorModel = null;
+        try {
+          vectorStream.close();
+        } catch (IOException ignored) {
+        }
+        vectorStream = null;
         return;
       }

Or restructure so a single outer try/finally covers both parser setup and the parse loop.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@vectormaster/src/main/java/org/appdevforall/codeonthego/vectormaster/VectorMasterView.java`
around lines 99 - 117, In VectorMasterView (method where vectorStream is used)
the XmlPullParserException path can return before the outer finally that closes
vectorStream, leaking the FileInputStream; update the catch block around
XmlPullParserFactory/newPullParser()/xpp.setInput(...) to explicitly close
vectorStream (or refactor so a single outer try/finally encloses both parser
setup and parsing) before setting vectorModel = null and returning, ensuring
vectorStream is always closed even on parser-setup failures.
vectormaster/src/main/java/org/appdevforall/codeonthego/vectormaster/VectorMasterDrawable.java (1)

117-135: ⚠️ Potential issue | 🟡 Minor

vectorStream still leaks on parser-setup failure.

The new finally at Line 403 only runs after the main parse loop try at Line 144. When XmlPullParserException is thrown while constructing/configuring the parser (Line 123), the method returns at Line 127 without ever closing vectorStream, and also without closing it when resID != -1 / else paths reassign xpp (although those don't own the stream). This leaves the FileInputStream opened at Line 110 for the finalizer — the exact situation this PR is trying to avoid.

🛠️ Close the stream on the early-return path
     XmlPullParser xpp;
     if (vectorStream != null) {
       try {
         XmlPullParserFactory parserFactory = XmlPullParserFactory.newInstance();
         parserFactory.setNamespaceAware(true);
         xpp = parserFactory.newPullParser();
         xpp.setInput(vectorStream, "utf-8");
       } catch (XmlPullParserException xml) {
         Log.e(TAG, "Error from reading vector image");
         xml.printStackTrace();
         vectorModel = null;
+        try {
+          vectorStream.close();
+        } catch (IOException ignored) {
+        }
+        vectorStream = null;
         return;
       }

Alternatively, restructure so a single outer try/finally (or try-with-resources over a local InputStream) covers both parser setup and the parse loop.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@vectormaster/src/main/java/org/appdevforall/codeonthego/vectormaster/VectorMasterDrawable.java`
around lines 117 - 135, The parser-setup path can return early and leak the
FileInputStream (vectorStream); ensure vectorStream is closed on all
early-return paths by either wrapping the InputStream in a try-with-resources or
creating a single outer try/finally that closes vectorStream, and then move
XmlPullParserFactory/newPullParser setup and the main parse loop (xpp usage)
inside that scope; specifically update the code handling vectorStream,
XmlPullParserFactory, xpp, and the early returns that set vectorModel to null so
that vectorStream.close() always runs (or is auto-closed) before returning,
while leaving resources.getXml(resID) behavior unchanged.
🧹 Nitpick comments (1)
app/src/main/java/com/itsaky/androidide/app/IDEApplication.kt (1)

214-218: Consider rate-limiting or sampling the Sentry report to avoid alert storms.

FinalizerWatchdogDaemon timeouts can fire repeatedly once the device's storage is slow (the watchdog is re-armed after each timeout). Reporting every one to Sentry may flood the project with duplicates of the same underlying condition and make the signal harder to read — which is the same concern that led the sibling isNonFatalGcCleanupFailure branch right above to log-only. Since the goal here is to suppress the crash (not treat it as an actionable bug), consider either:

  • Capturing only once per process (guarded by an AtomicBoolean), or
  • Attaching a fingerprint / tag (e.g. fingerprint = ["finalizer-watchdog-timeout"]) and letting Sentry group them, or
  • Using Sentry.captureMessage(...) at WARNING level instead of captureException, so stack-trace-based grouping doesn't split events across many issues.

Based on learnings (PR 957, IDEApplication.kt): the team previously chose to log-only for isNonFatalGcCleanupFailure to avoid reopening tickets for non-fatal errors — worth considering whether the same rationale applies here.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/com/itsaky/androidide/app/IDEApplication.kt` around lines
214 - 218, The FinalizerWatchdogDaemon branch currently calls
Sentry.captureException for every occurrence (inside
isFinalizerWatchdogTimeout), which can flood Sentry; change this to a
rate-limited or grouped report: for example, add an AtomicBoolean (e.g.,
finalizerWatchdogReported) and only call Sentry.captureException once per
process when isFinalizerWatchdogTimeout(thread, exception) is true, or instead
call Sentry.captureMessage with WARNING level and attach a static
fingerprint/tag like "finalizer-watchdog-timeout" so events are grouped; update
the code around the isFinalizerWatchdogTimeout check (where logger.warn and
Sentry.captureException are invoked) and mirror the non-fatal handling used for
isNonFatalGcCleanupFailure to avoid repeated identical reports.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/fragments/resources/ColorFragment.java`:
- Around line 84-91: The loadColorsFromXML method currently catches IOException
which swallows FileNotFoundException declared on the signature and prevents the
caller's error snackbar; update loadColorsFromXML in ColorFragment so
FileNotFoundException is allowed to propagate: either add a specific catch for
FileNotFoundException that rethrows it (or remove FileNotFoundException from
being caught by catching IOException after rethrow), and keep a separate catch
for other IOExceptions to log/handle them; locate the InputStream creation and
the try-with-resources in loadColorsFromXML and ensure FileNotFoundException is
not consumed there.

In
`@layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/fragments/resources/StringFragment.java`:
- Around line 78-85: The FileNotFoundException thrown by new FileInputStream is
currently swallowed by the catch(IOException) block so the caller no longer sees
the error; modify loadStringsFromXML to preserve caller-visible
FileNotFoundException by adding a specific catch(FileNotFoundException e) that
rethrows e (so the method still declares throws FileNotFoundException), and keep
a separate catch(IOException e) to handle and log other IO failures (e.g.,
close-time errors) while leaving stringParser and stringList assignment as is;
reference loadStringsFromXML, ValuesResourceParser, stringParser, and stringList
when making the change.

---

Outside diff comments:
In
`@vectormaster/src/main/java/org/appdevforall/codeonthego/vectormaster/VectorMasterDrawable.java`:
- Around line 117-135: The parser-setup path can return early and leak the
FileInputStream (vectorStream); ensure vectorStream is closed on all
early-return paths by either wrapping the InputStream in a try-with-resources or
creating a single outer try/finally that closes vectorStream, and then move
XmlPullParserFactory/newPullParser setup and the main parse loop (xpp usage)
inside that scope; specifically update the code handling vectorStream,
XmlPullParserFactory, xpp, and the early returns that set vectorModel to null so
that vectorStream.close() always runs (or is auto-closed) before returning,
while leaving resources.getXml(resID) behavior unchanged.

In
`@vectormaster/src/main/java/org/appdevforall/codeonthego/vectormaster/VectorMasterView.java`:
- Around line 99-117: In VectorMasterView (method where vectorStream is used)
the XmlPullParserException path can return before the outer finally that closes
vectorStream, leaking the FileInputStream; update the catch block around
XmlPullParserFactory/newPullParser()/xpp.setInput(...) to explicitly close
vectorStream (or refactor so a single outer try/finally encloses both parser
setup and parsing) before setting vectorModel = null and returning, ensuring
vectorStream is always closed even on parser-setup failures.

---

Nitpick comments:
In `@app/src/main/java/com/itsaky/androidide/app/IDEApplication.kt`:
- Around line 214-218: The FinalizerWatchdogDaemon branch currently calls
Sentry.captureException for every occurrence (inside
isFinalizerWatchdogTimeout), which can flood Sentry; change this to a
rate-limited or grouped report: for example, add an AtomicBoolean (e.g.,
finalizerWatchdogReported) and only call Sentry.captureException once per
process when isFinalizerWatchdogTimeout(thread, exception) is true, or instead
call Sentry.captureMessage with WARNING level and attach a static
fingerprint/tag like "finalizer-watchdog-timeout" so events are grouped; update
the code around the isFinalizerWatchdogTimeout check (where logger.warn and
Sentry.captureException are invoked) and mirror the non-fatal handling used for
isNonFatalGcCleanupFailure to avoid repeated identical reports.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f3b5d5d2-b7b4-4f59-9c7e-aac6409615d0

📥 Commits

Reviewing files that changed from the base of the PR and between 88d2f4a and 0deaf3c.

📒 Files selected for processing (8)
  • app/src/main/java/com/itsaky/androidide/app/IDEApplication.kt
  • composite-builds/build-deps/java-compiler/src/main/java/com/itsaky/androidide/zipfs2/ZipFileSystem.java
  • idetooltips/src/main/java/com/itsaky/androidide/idetooltips/ToolTipManager.kt
  • layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/fragments/resources/ColorFragment.java
  • layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/fragments/resources/StringFragment.java
  • layouteditor/src/main/java/org/appdevforall/codeonthego/layouteditor/managers/ValuesManager.java
  • vectormaster/src/main/java/org/appdevforall/codeonthego/vectormaster/VectorMasterDrawable.java
  • vectormaster/src/main/java/org/appdevforall/codeonthego/vectormaster/VectorMasterView.java

…to comply with declaration and snackbar usage
@hal-eisen-adfa hal-eisen-adfa requested a review from a team April 24, 2026 06:37
Copy link
Copy Markdown
Contributor

@Daniel-ADFA Daniel-ADFA left a comment

Choose a reason for hiding this comment

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

LGTM with a tiny comment :)

@hal-eisen-adfa hal-eisen-adfa merged commit 9726e17 into stage Apr 24, 2026
2 checks passed
@hal-eisen-adfa hal-eisen-adfa deleted the ADFA-2974-another-TimeoutException-attempt branch April 24, 2026 17:05
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