Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

java.util.concurrent.TimeUnit vs java.time.Duration for timeout arguments

Tags:

java

java-8

When writing an API, I tend to define methods that take a timeout parameter as having arguments long timeout, TimeUnit unit. This follows many java.util.concurrent methods that have the same requirement.

In Java 8, I've noticed the new Duration class in the java.time package, and wonder if I might as well define new methods as having a Duration timeout argument.

Advantage I can see from this are:

  • It's is easier for users to write constants:

    private static final Duration ACQUIRE_TIMEOUT = Duration.ofMinutes(10);
    
    someResource.acquire(ACQUIRE_TIMEOUT);
    

    vs

    private static final long ACQUIRE_TIMEOUT_MILLIS = TimeUnit.MINUTES.toMillis(10);
    
    someResource.acquire(ACQUIRE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
    

But here's a possible problem:

  • How do users specify infinite time?

    someResource.acquire(Long.MAX_VALUE, TimeUnit.MILLISECONDS); // No timeout
    

    vs

    someResource.acquire(Duration.ofSeconds(Long.MAX_VALUE)); // Is this how it's done?
    

    where acquire(Duration) could be implemented as:

    public boolean acquire(Duration timeout) {
        long millis;
        try { millis = timeout.toMillis(); }
        catch (ArithmeticException ignore) { millis = Long.MAX_VALUE; } // yuck!
    
        ...
    }
    

    Definitely not the prettiest code.

Is there a better way of doing this? Are there other gotchas or reasons I shouldn't use java.time.Duration for timeout arguments in APIs I provide?

like image 359
antak Avatar asked Sep 01 '15 01:09

antak


1 Answers

Your code

private static final long ACQUIRE_TIMEOUT_MILLIS = TimeUnit.MINUTES.toMillis(10);
someResource.acquire(ACQUIRE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);

counteracts the intended use of providing a value in an application specific unit together with that unit. The intended use should be someResource.acquire(10, TimeUnit.MINUTES);

But it shows the problem with this approach as it is impossible to define a single constant bearing a value and a TimeUnit. So Duration has a point here.

Since TimeUnit is the well-known type created for concurrency APIs and used by the backend code in most cases, I would never omit the methods with the (long,TimeUnit) signature, but providing overloads accepting a Duration is not wrong.


When it comes to the “no timeout” question, most APIs don’t have a special treatment for Long.MAX_VALUE, regardless of whether it is provided as seconds or milliseconds. The observed “no timeout” behavior results from the fact that both quantities, (Long.MAX_VALUE, TimeUnit.SECONDS) and (Long.MAX_VALUE, TimeUnit.MILLISECONDS) denote a time, none of today’s computers will survive. We can be happy if mankind lasts that long. Using Long.MAX_VALUE-1 would exhibit the same behavior.

By the way, some concurrency tools unconditionally convert timeouts to the nanosecond scale internally, which implies that the maximum waiting time is limited to about 300 years “only”, but I assume that for most applications that still equals to “practically no timeout”.

In other words, there is no practical difference between Duration.ofSeconds(Long.MAX_VALUE) and Long.MAX_VALUE, TimeUnit.MILLISECONDS when used as a timeout in a runtime operation.


Addendum: I first overlooked your question regarding the practical implementation. I recommend to follow the behavior of converting to nanoseconds described above rather than using milliseconds. The best thing I can come up with is

public boolean acquire(long timeout, TimeUnit timeUnit) {
    // actual implementation
}
static final Duration MAX_WAIT = Duration.ofNanos(Long.MAX_VALUE);
public boolean acquire(Duration timeout) {
    return acquire(
        timeout.compareTo(MAX_WAIT)>=0? Long.MAX_VALUE: timeout.toNanos(),
        TimeUnit.NANOSECONDS);
}

It would be possible to do a similar thing with milliseconds, however, I think in the future the imprecision of milliseconds over nanoseconds is more likely to become an issue than the maximum timeout being limited to something above 292 years.

like image 179
Holger Avatar answered Nov 05 '22 05:11

Holger