View Javadoc

1   /**
2    * Copyright 2008 Atlassian Pty Ltd 
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License"); 
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at 
7    * 
8    *     http://www.apache.org/licenses/LICENSE-2.0 
9    * 
10   * Unless required by applicable law or agreed to in writing, software 
11   * distributed under the License is distributed on an "AS IS" BASIS, 
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
13   * See the License for the specific language governing permissions and 
14   * limitations under the License.
15   */
16  
17  package com.atlassian.util.concurrent;
18  
19  import java.util.concurrent.TimeUnit;
20  import java.util.concurrent.TimeoutException;
21  import java.util.concurrent.locks.Condition;
22  
23  import net.jcip.annotations.Immutable;
24  
25  /**
26   * Used to calculate elapsed time for timeouts from when it is created when
27   * successively calling blocking methods. Always converts to nanoseconds.
28   * <p>
29   * Usage:
30   * 
31   * <pre>
32   * Timeout timeout = Timeout.getNanosTimeout(1, TimeUnit.SECONDS);
33   * String str = futureString.get(timeout.getTime(), timeout.getUnit());
34   * Integer num = futureInt.get(timeout.getTime(), timeout.getUnit());
35   * </pre>
36   * 
37   * where if the first call takes quarter of a second, the second call is passed
38   * the equivalent of three-quarters of a second.
39   */
40  @Immutable
41  public class Timeout {
42  
43      private static final TimeSupplier NANO_SUPPLIER = new TimeSupplier() {
44          public long currentTime() {
45              return System.nanoTime();
46          };
47  
48          public TimeUnit precision() {
49              return TimeUnit.NANOSECONDS;
50          };
51      };
52  
53      private static final TimeSupplier MILLIS_SUPPLIER = new TimeSupplier() {
54          public long currentTime() {
55              return System.currentTimeMillis();
56          };
57  
58          public TimeUnit precision() {
59              return TimeUnit.MILLISECONDS;
60          };
61      };
62  
63      /**
64       * Get a {@link Timeout} that uses nanosecond precision. The accuracy will
65       * depend on the accuracy of {@link System#nanoTime()}.
66       * 
67       * @param time the maximum time to wait for the lock
68       * @param unit the time unit of the <tt>time</tt> argument.
69       * @return timeout with {@link TimeUnit#NANOSECONDS} precision.
70       */
71      public static Timeout getNanosTimeout(final long time, final TimeUnit unit) {
72          return new Timeout(time, unit, NANO_SUPPLIER);
73      }
74  
75      /**
76       * Get a {@link Timeout} that uses nanosecond precision. The accuracy will
77       * depend on the accuracy of {@link System#nanoTime()}.
78       * 
79       * @param time the maximum time to wait for the lock
80       * @param unit the time unit of the <tt>time</tt> argument.
81       * @return timeout with {@link TimeUnit#NANOSECONDS} precision.
82       */
83      public static Timeout getMillisTimeout(final long time, final TimeUnit unit) {
84          return new Timeout(time, unit, MILLIS_SUPPLIER);
85      }
86  
87      private final long created;
88      private final long time;
89      private final TimeSupplier supplier;
90  
91      Timeout(final long time, final TimeUnit unit, final TimeSupplier supplier) {
92          created = supplier.currentTime();
93          this.supplier = supplier;
94          this.time = this.supplier.precision().convert(time, unit);
95      }
96  
97      public long getTime() {
98          return (created + time) - supplier.currentTime();
99      }
100 
101     public TimeUnit getUnit() {
102         return supplier.precision();
103     }
104 
105     /**
106      * Has this timeout expired
107      * 
108      * @return
109      */
110     public boolean isExpired() {
111         return getTime() <= 0;
112     }
113 
114     //
115     // util
116     //
117 
118     void await(final Awaitable waitable) throws TimeoutException, InterruptedException {
119         if (!waitable.await(getTime(), getUnit())) {
120             throw new TimedOutException(this);
121         }
122     }
123 
124     // TODO unused, retire?
125     // /CLOVER:OFF
126     void await(final Condition condition) throws TimeoutException, InterruptedException {
127         if (!condition.await(getTime(), getUnit())) {
128             throw new TimedOutException(this);
129         }
130     }
131 
132     // /CLOVER:ON
133 
134     /**
135      * Supply time and precision to a {@link Timeout}.
136      */
137     interface TimeSupplier {
138         long currentTime();
139 
140         TimeUnit precision();
141     }
142 }