{"record":{"id":"9797ac8a0a24a0ff","repo":"Tencent/matrix","slug":"did-not-expect-thread-to-be-interrupted","errorCode":null,"errorMessage":"Did not expect thread to be interrupted","messagePattern":"Did not expect thread to be interrupted","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"matrix/matrix-android/matrix-resource-canary/matrix-resource-canary-android/src/main/java/com/tencent/matrix/resource/leakcanary/internal/FutureResult.java","lineNumber":36,"sourceCode":"import java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicReference;\n\npublic final class FutureResult<T> {\n\n    private final AtomicReference<T> resultHolder;\n    private final CountDownLatch latch;\n\n    public FutureResult() {\n        resultHolder = new AtomicReference<>();\n        latch = new CountDownLatch(1);\n    }\n\n    public boolean wait(long timeout, TimeUnit unit) {\n        try {\n            return latch.await(timeout, unit);\n        } catch (InterruptedException e) {\n            throw new RuntimeException(\"Did not expect thread to be interrupted\", e);\n        }\n    }\n\n    public T get() {\n        if (latch.getCount() > 0) {\n            throw new IllegalStateException(\"Call wait() and check its result\");\n        }\n        return resultHolder.get();\n    }\n\n    public void set(T result) {\n        resultHolder.set(result);\n        latch.countDown();\n    }\n}\n","sourceCodeStart":18,"sourceCodeEnd":52,"githubUrl":"https://github.com/Tencent/matrix/blob/3b8293bd65d47eeea7caf1f32a3a5d4d5eab60e7/matrix/matrix-android/matrix-resource-canary/matrix-resource-canary-android/src/main/java/com/tencent/matrix/resource/leakcanary/internal/FutureResult.java#L18-L52","documentation":"FutureResult.wait wraps CountDownLatch.await and treats InterruptedException as a programming error: the dumping thread is not expected to be interrupted while waiting for the heap-dump worker. When the waiting thread's interrupt flag is set, it rethrows as RuntimeException('Did not expect thread to be interrupted'). This keeps leak-detection logic simple by assuming no cooperative cancellation.","triggerScenarios":"Calling wait(timeout, unit) (from dumpHeap) after the waiting thread has been interrupted, e.g. Thread.interrupt() issued by an executor shutdown, Activity destroy path, or watchdog canceling the dump.","commonSituations":"App shutting down or ViewModel/Activity being destroyed while a heap dump is in flight; executor.shutdownNow() interrupting worker threads; a developer canceling a dump task with future.cancel(true).","solutions":["Avoid interrupting the thread performing the heap dump; use a cooperative cancel flag instead.","Ensure dumpHeap runs on a dedicated thread not subject to shutdownNow() interrupts.","If interruption is legitimate in your flow, call wait inside a try-catch for RuntimeException and treat it as 'dump aborted'.","Check that no other component (watchdog, leak cancel API) interrupts this thread concurrently."],"exampleFix":"// before\nthreadPool.shutdownNow(); // interrupts in-flight dump\n// after\ndumpFuture.cancel(false); // let running dump finish, then shutdown","handlingStrategy":"try-catch","validationCode":"if (Thread.currentThread().isInterrupted()) {\n    // clear/defer interrupt before calling wait\n    Thread.interrupted();\n}","typeGuard":null,"tryCatchPattern":"try {\n    boolean ok = futureResult.wait(10, TimeUnit.SECONDS);\n} catch (RuntimeException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"interrupted\")) {\n        // treat as dump aborted; restore interrupt status\n        Thread.currentThread().interrupt();\n    }\n}","preventionTips":["Never shutdownNow()/interrupt the thread performing heap dumps","Use cooperative cancel flags instead of interrupts","Run dumps on a dedicated executor isolated from lifecycle shutdown"],"tags":["interruption","concurrency","heap-dump","threads"],"backgroundTag":"thread-interrupted","analyzedSha":"3b8293bd65d47eeea7caf1f32a3a5d4d5eab60e7","analyzedAt":"2026-09-08T08:01:39.722Z","contentChangedAt":"2026-09-08T08:01:39.722Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}