1 /*
2  * Copyright (C) 2017 The Android Open Source Project
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.android.dialer.common.backoff;
18 
19 import com.android.dialer.common.Assert;
20 
21 /**
22  * Given an initial backoff delay, D, a base multiplier, B, and a total number of backoffs, N, this
23  * class returns values in the exponential sequence, D, D*B, D*B^2, ... D*B^(N-1), ...
24  *
25  * <p>Example usage:
26  *
27  * <pre>
28  *   long initialDelayMillis = 1000;
29  *   double multiplier = 1.2;
30  *   int backoffs = 10;
31  *   ExponentialBackoff backoff = new ExponentialBackoff(initialDelayMillis, multiplier, backoffs);
32  *   while (backoff.isInRange()) {
33  *     ...
34  *     sleep(backoff.getNextBackoff());
35  *   }
36  * </pre>
37  *
38  * <p>Note: the base multiplier can be calculated using {@code ExponentialBaseCalculator}
39  */
40 public final class ExponentialBackoff {
41   public final long initialDelayMillis;
42   public final double baseMultiplier;
43   public final int maximumBackoffs;
44   private double nextBackoff;
45   private int backoffCount;
46 
47   /**
48    * Setup an exponential backoff with an initial delay, a base multiplier and a maximum number of
49    * backoff steps.
50    *
51    * @throws IllegalArgumentException for negative argument values
52    */
ExponentialBackoff(long initialDelayMillis, double baseMultiplier, int maximumBackoffs)53   public ExponentialBackoff(long initialDelayMillis, double baseMultiplier, int maximumBackoffs) {
54     Assert.checkArgument(initialDelayMillis > 0);
55     Assert.checkArgument(baseMultiplier > 0);
56     Assert.checkArgument(maximumBackoffs > 0);
57     this.initialDelayMillis = initialDelayMillis;
58     this.baseMultiplier = baseMultiplier;
59     this.maximumBackoffs = maximumBackoffs;
60     reset();
61   }
62 
63   /**
64    * @return the next backoff time in the exponential sequence. Specifically, if D is the initial
65    *     delay, B is the base multiplier and N is the total number of backoffs, then the return
66    *     values will be: D, D*B, D*B^2, ... D*B^(N-1), ...
67    */
getNextBackoff()68   public long getNextBackoff() {
69     long backoff = Math.round(nextBackoff);
70     backoffCount++;
71     nextBackoff *= baseMultiplier;
72     return backoff;
73   }
74 
75   /** @return the number of times getNextBackoff() has been called */
getBackoffCount()76   public int getBackoffCount() {
77     return backoffCount;
78   }
79 
80   /**
81    * @return {@code true} if getNextBackoff() has been called less than the maximumBackoffs value
82    *     specified in the constructor.
83    */
isInRange()84   public boolean isInRange() {
85     return backoffCount < maximumBackoffs;
86   }
87 
88   /**
89    * Reset the sequence of backoff values so the next call to getNextBackoff() will return the
90    * initial delay and getBackoffCount() will return 0
91    */
reset()92   public void reset() {
93     nextBackoff = initialDelayMillis;
94     backoffCount = 0;
95   }
96 }
97