ADFA-3945: Handle reader-thread IOException on tar.xz extraction timeout#1412
ADFA-3945: Handle reader-thread IOException on tar.xz extraction timeout#1412fryanpan wants to merge 1 commit into
Conversation
9f4d410 to
1d4c800
Compare
|
This PR previously caught the reader-thread Rejected Option A (close-then-destroy): on Android, closing a stream under a blocked read is precisely what throws Branch was rewritten to a single clean commit ( |
…thread race) The Termux tar extraction drained the child's stdout on a separate reader thread. On a 2-minute timeout the code calls destroyForcibly(), which closes the pipe; the reader thread's in-flight read() then throws InterruptedIOException on a bare thread with no handler, crashing the app (Sentry APPDEVFORALL-V0). Fix (root cause, not the symptom): redirect the child's combined stdout/stderr to a temp file via ProcessBuilder.redirectOutput(), drop the reader thread entirely, and read the log back after waitFor(). With OS-side redirection there is no concurrent read to interrupt when the pipe is closed, and the process also can't deadlock on a full pipe buffer. destroyForcibly() on timeout is retained; the temp log is deleted after use. This eliminates the entire bug class rather than catching the exception, and makes a future timeout/cancellation change safe (no race to reintroduce). Per Hal's suggestion on ADFA-3945. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1d4c800 to
847c79e
Compare
|
Warning Review limit reached
More reviews will be available in 26 minutes and 57 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits. 🚦 How do rate limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthrough
Termux tar extraction output redirection
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
Jira Ticket: https://appdevforall.atlassian.net/browse/ADFA-3945
Sentry Issue: https://appdevforall-inc-9p.sentry.io/issues/APPDEVFORALL-V0
Reproduction Details
IdeArchiveServiceImpl.extractTarXzViaTermuxran Termuxtarand drained the child's stdout on a separate reader thread (thread { process.inputStream.bufferedReader().useLines { … } }). On a 2-minute timeout the code callsdestroyForcibly(), which closes the pipe; the reader thread's in-flightread()then throwsInterruptedIOExceptionon a bare thread with no handler → uncaught → app crash.Stack Trace
(thread:
tar-xz-extract-output· device.class: high)User Steps
User steps leading up to crash, based on Sentry breadcrumbs:
org.appdevforall.ndkinstaller.cgp). The extraction is force-killed (timeout/cancel) and the reader thread dies mid-read. (The crashing event itself has no UI breadcrumbs — it's a background reader thread.)Was able to reproduce in a unit test?
No.
What remains after this fix is the
ProcessBuilder/Termux path —extractTarXzViaTermuxis aprivate funon afinalclass and execs a hard-coded on-devicetarbinary ($TERMUX_BIN_PATH/tar) absent on any JVM test host, sostart()can't run there. However, Option B removes the concurrency hazard entirely (no reader thread), so the failure class this ticket is about no longer exists to be triggered. Making the remaining exec path regression-testable would need an injectable process/command seam (@VisibleForTesting) — tracked as a follow-up. The change compiles cleanly (:plugin-manager:compileV8DebugKotlinBUILD SUCCESSFUL).What Was Fixed
Root-cause fix (Hal's suggestion, "Option B"), not a symptom catch. Redirect the child's combined stdout/stderr to a temp file via
ProcessBuilder.redirectOutput(logFile)and read it back afterwaitFor(), dropping the reader thread entirely:read()to be interrupted whendestroyForcibly()closes the pipe — theInterruptedIOException-on-a-bare-thread crash cannot occur.destroyForcibly()on timeout is retained; the temp log is deleted after use.(Considered and rejected "Option A" — close stdout then
destroyForcibly(): on Android, closing a stream under a blocked read is exactly what producesread interrupted by close(), so it wouldn't reliably give a clean EOF and would still need the catch.)Testing
:plugin-manager:compileV8DebugKotlin→ BUILD SUCCESSFUL. No automated runtime test (see above — the remaining exec path isn't JVM-testable; the bug class itself is removed by construction). Branch history was rewritten to a single clean commit implementing Option B.Follow-up (separate): make the extraction timeout adaptive for slow devices + add progress/cancel UI + a retry path (the user-facing "extraction actually succeeds" improvement), and add an
@VisibleForTestingprocess seam.Fixes APPDEVFORALL-V0