Tencent/matrix · error · RuntimeException

Did not expect thread to be interrupted

Error message

Did not expect thread to be interrupted

What it means

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.

Solutions

  1. Avoid interrupting the thread performing the heap dump; use a cooperative cancel flag instead.
  2. Ensure dumpHeap runs on a dedicated thread not subject to shutdownNow() interrupts.
  3. If interruption is legitimate in your flow, call wait inside a try-catch for RuntimeException and treat it as 'dump aborted'.
  4. Check that no other component (watchdog, leak cancel API) interrupts this thread concurrently.

Example fix

// before
threadPool.shutdownNow(); // interrupts in-flight dump
// after
dumpFuture.cancel(false); // let running dump finish, then shutdown
Defensive patterns

Strategy: try-catch

Validate before calling

if (Thread.currentThread().isInterrupted()) {
    // clear/defer interrupt before calling wait
    Thread.interrupted();
}

Try / catch

try {
    boolean ok = futureResult.wait(10, TimeUnit.SECONDS);
} catch (RuntimeException e) {
    if (e.getMessage() != null && e.getMessage().contains("interrupted")) {
        // treat as dump aborted; restore interrupt status
        Thread.currentThread().interrupt();
    }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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).

Related errors


AI-assisted analysis of Tencent/matrix@3b8293bd65 (2026-09-08). Data as JSON: /api/errors/9797ac8a0a24a0ff. Report an issue: GitHub.

Appendix: source

Thrown at matrix/matrix-android/matrix-resource-canary/matrix-resource-canary-android/src/main/java/com/tencent/matrix/resource/leakcanary/internal/FutureResult.java:36

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

public final class FutureResult<T> {

    private final AtomicReference<T> resultHolder;
    private final CountDownLatch latch;

    public FutureResult() {
        resultHolder = new AtomicReference<>();
        latch = new CountDownLatch(1);
    }

    public boolean wait(long timeout, TimeUnit unit) {
        try {
            return latch.await(timeout, unit);
        } catch (InterruptedException e) {
            throw new RuntimeException("Did not expect thread to be interrupted", e);
        }
    }

    public T get() {
        if (latch.getCount() > 0) {
            throw new IllegalStateException("Call wait() and check its result");
        }
        return resultHolder.get();
    }

    public void set(T result) {
        resultHolder.set(result);
        latch.countDown();
    }
}

View on GitHub (pinned to 3b8293bd65)