1 /*
2 * Copyright (C) 2014 Square, Inc.
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 package okio;
17
18 import java.io.IOException;
19 import java.io.InterruptedIOException;
20 import java.util.concurrent.TimeUnit;
21
22 /**
23 * A policy on how much time to spend on a task before giving up. When a task
24 * times out, it is left in an unspecified state and should be abandoned. For
25 * example, if reading from a source times out, that source should be closed and
26 * the read should be retried later. If writing to a sink times out, the same
27 * rules apply: close the sink and retry later.
28 *
29 * <h3>Timeouts and Deadlines</h3>
30 * This class offers two complementary controls to define a timeout policy.
31 *
32 * <p><strong>Timeouts</strong> specify the maximum time to wait for a single
33 * operation to complete. Timeouts are typically used to detect problems like
34 * network partitions. For example, if a remote peer doesn't return <i>any</i>
35 * data for ten seconds, we may assume that the peer is unavailable.
36 *
37 * <p><strong>Deadlines</strong> specify the maximum time to spend on a job,
38 * composed of one or more operations. Use deadlines to set an upper bound on
39 * the time invested on a job. For example, a battery-conscious app may limit
40 * how much time it spends pre-loading content.
41 */
42 public class Timeout {
43 /**
44 * An empty timeout that neither tracks nor detects timeouts. Use this when
45 * timeouts aren't necessary, such as in implementations whose operations
46 * do not block.
47 */
48 public static final Timeout NONE = new Timeout() {
49 @Override public Timeout timeout(long timeout, TimeUnit unit) {
50 return this;
51 }
52
53 @Override public Timeout deadlineNanoTime(long deadlineNanoTime) {
54 return this;
55 }
56
57 @Override public void throwIfReached() throws IOException {
58 }
59 };
60
61 /**
62 * True if {@code deadlineNanoTime} is defined. There is no equivalent to null
63 * or 0 for {@link System#nanoTime}.
64 */
65 private boolean hasDeadline;
66 private long deadlineNanoTime;
67 private long timeoutNanos;
68
69 public Timeout() {
70 }
71
72 /**
73 * Wait at most {@code timeout} time before aborting an operation. Using a
74 * per-operation timeout means that as long as forward progress is being made,
75 * no sequence of operations will fail.
76 *
77 * <p>If {@code timeout == 0}, operations will run indefinitely. (Operating
78 * system timeouts may still apply.)
79 */
80 public Timeout timeout(long timeout, TimeUnit unit) {
81 if (timeout < 0) throw new IllegalArgumentException("timeout < 0: " + timeout);
82 if (unit == null) throw new IllegalArgumentException("unit == null");
83 this.timeoutNanos = unit.toNanos(timeout);
84 return this;
85 }
86
87 /** Returns the timeout in nanoseconds, or {@code 0} for no timeout. */
88 public long timeoutNanos() {
89 return timeoutNanos;
90 }
91
92 /** Returns true if a deadline is enabled. */
93 public boolean hasDeadline() {
94 return hasDeadline;
95 }
96
97 /**
98 * Returns the {@linkplain System#nanoTime() nano time} when the deadline will
99 * be reached.
100 *
101 * @throws IllegalStateException if no deadline is set.
102 */
103 public long deadlineNanoTime() {
104 if (!hasDeadline) throw new IllegalStateException("No deadline");
105 return deadlineNanoTime;
106 }
107
108 /**
109 * Sets the {@linkplain System#nanoTime() nano time} when the deadline will be
110 * reached. All operations must complete before this time. Use a deadline to
111 * set a maximum bound on the time spent on a sequence of operations.
112 */
113 public Timeout deadlineNanoTime(long deadlineNanoTime) {
114 this.hasDeadline = true;
115 this.deadlineNanoTime = deadlineNanoTime;
116 return this;
117 }
118
119 /** Set a deadline of now plus {@code duration} time. */
120 public final Timeout deadline(long duration, TimeUnit unit) {
121 if (duration <= 0) throw new IllegalArgumentException("duration <= 0: " + duration);
122 if (unit == null) throw new IllegalArgumentException("unit == null");
123 return deadlineNanoTime(System.nanoTime() + unit.toNanos(duration));
124 }
125
126 /** Clears the timeout. Operating system timeouts may still apply. */
127 public Timeout clearTimeout() {
128 this.timeoutNanos = 0;
129 return this;
130 }
131
132 /** Clears the deadline. */
133 public Timeout clearDeadline() {
134 this.hasDeadline = false;
135 return this;
136 }
137
138 /**
139 * Throws an {@link InterruptedIOException} if the deadline has been reached or if the current
140 * thread has been interrupted. This method doesn't detect timeouts; that should be implemented to
141 * asynchronously abort an in-progress operation.
142 */
143 public void throwIfReached() throws IOException {
144 if (Thread.interrupted()) {
145 Thread.currentThread().interrupt(); // Retain interrupted status.
146 throw new InterruptedIOException("interrupted");
147 }
148
149 if (hasDeadline && deadlineNanoTime - System.nanoTime() <= 0) {
150 throw new InterruptedIOException("deadline reached");
151 }
152 }
153
154 /**
155 * Waits on {@code monitor} until it is notified. Throws {@link InterruptedIOException} if either
156 * the thread is interrupted or if this timeout elapses before {@code monitor} is notified. The
157 * caller must be synchronized on {@code monitor}.
158 *
159 * <p>Here's a sample class that uses {@code waitUntilNotified()} to await a specific state. Note
160 * that the call is made within a loop to avoid unnecessary waiting and to mitigate spurious
161 * notifications. <pre>{@code
162 *
163 * class Dice {
164 * Random random = new Random();
165 * int latestTotal;
166 *
167 * public synchronized void roll() {
168 * latestTotal = 2 + random.nextInt(6) + random.nextInt(6);
169 * System.out.println("Rolled " + latestTotal);
170 * notifyAll();
171 * }
172 *
173 * public void rollAtFixedRate(int period, TimeUnit timeUnit) {
174 * Executors.newScheduledThreadPool(0).scheduleAtFixedRate(new Runnable() {
175 * public void run() {
176 * roll();
177 * }
178 * }, 0, period, timeUnit);
179 * }
180 *
181 * public synchronized void awaitTotal(Timeout timeout, int total)
182 * throws InterruptedIOException {
183 * while (latestTotal != total) {
184 * timeout.waitUntilNotified(this);
185 * }
186 * }
187 * }
188 * }</pre>
189 */
190 public final void waitUntilNotified(Object monitor) throws InterruptedIOException {
191 try {
192 boolean hasDeadline = hasDeadline();
193 long timeoutNanos = timeoutNanos();
194
195 if (!hasDeadline && timeoutNanos == 0L) {
196 monitor.wait(); // There is no timeout: wait forever.
197 return;
198 }
199
200 // Compute how long we'll wait.
201 long waitNanos;
202 long start = System.nanoTime();
203 if (hasDeadline && timeoutNanos != 0) {
204 long deadlineNanos = deadlineNanoTime() - start;
205 waitNanos = Math.min(timeoutNanos, deadlineNanos);
206 } else if (hasDeadline) {
207 waitNanos = deadlineNanoTime() - start;
208 } else {
209 waitNanos = timeoutNanos;
210 }
211
212 // Attempt to wait that long. This will break out early if the monitor is notified.
213 long elapsedNanos = 0L;
214 if (waitNanos > 0L) {
215 long waitMillis = waitNanos / 1000000L;
216 monitor.wait(waitMillis, (int) (waitNanos - waitMillis * 1000000L));
217 elapsedNanos = System.nanoTime() - start;
218 }
219
220 // Throw if the timeout elapsed before the monitor was notified.
221 if (elapsedNanos >= waitNanos) {
222 throw new InterruptedIOException("timeout");
223 }
224 } catch (InterruptedException e) {
225 Thread.currentThread().interrupt(); // Retain interrupted status.
226 throw new InterruptedIOException("interrupted");
227 }
228 }
229
230 static long minTimeout(long aNanos, long bNanos) {
231 if (aNanos == 0L) return bNanos;
232 if (bNanos == 0L) return aNanos;
233 if (aNanos < bNanos) return aNanos;
234 return bNanos;
235 }
236 }
237