{"record":{"id":"f7d372be7eccb1df","repo":"Tencent/matrix","slug":"call-wait-and-check-its-result","errorCode":null,"errorMessage":"Call wait() and check its result","messagePattern":"Call wait\\(\\) and check its result","errorType":"exception","errorClass":"IllegalStateException","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":42,"sourceCode":"    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":24,"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#L24-L52","documentation":"FutureResult.get returns the computed result but requires wait() to have completed first; it checks latch.getCount() and throws IllegalStateException if the latch hasn't counted down to zero. This guards against reading resultHolder before the producer thread has set it, which would return null/garbage silently.","triggerScenarios":"Calling get() on a FutureResult without first calling wait(timeout, unit) and confirming it returned true, e.g. calling get() immediately after enqueueing the heap dump task.","commonSituations":"Misusing FutureResult as a plain holder; forgetting the wait step after copying sample code; calling get() from a different thread that skipped the wait timeout.","solutions":["Always call wait(timeout, TimeUnit) and check its boolean result before calling get().","If wait() returned false (timeout), handle the timeout instead of calling get().","Use the same thread flow as the library's dumpHeap: wait, then get.","If you need async semantics, wrap wait+get in your own future/callback rather than splitting them across threads."],"exampleFix":"// before\nFutureResult<HeapDump> fr = dumpHeapAsync();\nHeapDump dump = fr.get();\n// after\nFutureResult<HeapDump> fr = dumpHeapAsync();\nif (fr.wait(10, TimeUnit.SECONDS)) {\n    HeapDump dump = fr.get();\n}","handlingStrategy":"validation","validationCode":"if (!futureResult.wait(10, TimeUnit.SECONDS)) {\n    throw new TimeoutException(\"heap dump did not finish\");\n}\nT result = futureResult.get();","typeGuard":null,"tryCatchPattern":"try {\n    T result = futureResult.get();\n} catch (IllegalStateException e) {\n    // wait() was skipped or timed out; redo wait or abort\n}","preventionTips":["Always pair get() with a preceding successful wait() call","Check wait()'s boolean return before get()","Keep wait+get on the same call path/thread"],"tags":["concurrency","state-machine","illegal-state","future"],"backgroundTag":"invalid-state-transition","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"}