I'm trying to implement Future.get(long, TimeUnit) in terms of TimeUnit.timedWait(Object, long).
It's not clear how to use TimeUnit.timedWait(Object, long)
in a way that handles spurious wakeups without losing the nanosecond component of TimeUnit. Normally you'd do something like this:
public V get(long timeout, TimeUnit unit)
{
long expirationTime = new Date().getTime() + unit.toMillis(timeout);
while (!condition)
{
long timeLeft = expirationTime - new Date().getTime();
if (timeLeft <= 0)
throw new TimeoutException();
unit.timedWait(object, timeLeft);
}
}
but you lose the nanosecond component. If everyone is simply dropping the nanosecond component then what is the point of TimeUnit
even supporting nanoseconds and offering TimeUnit.timedWait()
?
CountDownLatch seems to be the easiest way to implement this:
public class MutableFuture<T> implements Future<T>
{
private final CountDownLatch done = new CountDownLatch(1);
private T value;
private Throwable throwable;
public synchronized boolean isDone()
{
return done.getCount() == 0;
}
public synchronized T get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException
{
if (!done.await(timeout, unit))
throw new TimeoutException();
if (throwable != null)
throw new ExecutionException(throwable);
return value;
}
// remaining methods left as exercise to the reader :)
}
CountdownLatch is not vulnerable to spurious wakeups (because it can check the latch state internally before returning).