Home | History | Annotate | Download | only in okio
      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 preloading 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       throw new InterruptedIOException("thread interrupted");
    146     }
    147 
    148     if (hasDeadline && deadlineNanoTime - System.nanoTime() <= 0) {
    149       throw new InterruptedIOException("deadline reached");
    150     }
    151   }
    152 }
    153