Search code examples
javaconcurrencytime-wait

How to use TimeUnit.timedWait() without losing nanosecond precision?


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()?


Solution

  • 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).