Problem
FutureImpl._await calls tryComplete() on timeout and interrupt, permanently marking the shared Future as failed for all consumers - even though the underlying computation may still succeed.
Timeout poisoning
If thread T1 calls f.await(1, SECONDS) and T2 calls f.await(10, SECONDS), T1's shorter timeout permanently marks the Future as failed with TimeoutException for ALL consumers, including T2. The actual computation result is discarded.
// FutureImpl.java, _await -> block()
if (System.nanoTime() - start > duration) {
tryComplete(Try.failure(new TimeoutException(...)));
}
Interrupt poisoning
Same pattern: an interrupt to any awaiting thread calls tryComplete(Try.failure(new ExecutionException(new InterruptedException()))), permanently failing the Future for all consumers.
if (waitingThread.isInterrupted()) {
tryComplete(Try.failure(new ExecutionException(new InterruptedException())));
}
Expected behavior
Timeout and interrupt are local to the waiting thread - they mean "I don't want to wait anymore", not "the Future has failed." _await should return without calling tryComplete(), leaving the Future in its current state.
Callers can use isCompleted() to distinguish between completion and timeout.
Breaking change
This is a semver-major change. The current (buggy) behavior is documented in Future.await's Javadoc and existing code may rely on future.await(timeout).isFailure() returning true with a TimeoutException cause to detect timeouts. After the fix, callers would need to use !future.await(timeout).isCompleted() instead.
Fix
In FutureImpl._await's block() method, replace both tryComplete(...) calls (timeout + interrupt) with return true to exit the blocking loop without poisoning the Future.
Problem
FutureImpl._awaitcallstryComplete()on timeout and interrupt, permanently marking the shared Future as failed for all consumers - even though the underlying computation may still succeed.Timeout poisoning
If thread T1 calls
f.await(1, SECONDS)and T2 callsf.await(10, SECONDS), T1's shorter timeout permanently marks the Future as failed withTimeoutExceptionfor ALL consumers, including T2. The actual computation result is discarded.Interrupt poisoning
Same pattern: an interrupt to any awaiting thread calls
tryComplete(Try.failure(new ExecutionException(new InterruptedException()))), permanently failing the Future for all consumers.Expected behavior
Timeout and interrupt are local to the waiting thread - they mean "I don't want to wait anymore", not "the Future has failed."
_awaitshould return without callingtryComplete(), leaving the Future in its current state.Callers can use
isCompleted()to distinguish between completion and timeout.Breaking change
This is a semver-major change. The current (buggy) behavior is documented in
Future.await's Javadoc and existing code may rely onfuture.await(timeout).isFailure()returningtruewith aTimeoutExceptioncause to detect timeouts. After the fix, callers would need to use!future.await(timeout).isCompleted()instead.Fix
In
FutureImpl._await'sblock()method, replace bothtryComplete(...)calls (timeout + interrupt) withreturn trueto exit the blocking loop without poisoning the Future.