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 */ 16package okio; 17 18import java.io.IOException; 19import java.io.InterruptedIOException; 20import 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 preloading content. 41 */ 42public 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 throw new InterruptedIOException("thread interrupted"); 146 } 147 148 if (hasDeadline && deadlineNanoTime - System.nanoTime() <= 0) { 149 throw new InterruptedIOException("deadline reached"); 150 } 151 } 152} 153