Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / cacheinvalidation / src / java / com / google / ipc / invalidation / ticl / RecurringTask.java
1 /*
2  * Copyright 2011 Google 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
17 package com.google.ipc.invalidation.ticl;
18
19 import com.google.ipc.invalidation.external.client.SystemResources.Logger;
20 import com.google.ipc.invalidation.external.client.SystemResources.Scheduler;
21 import com.google.ipc.invalidation.ticl.proto.Client.ExponentialBackoffState;
22 import com.google.ipc.invalidation.ticl.proto.JavaClient.RecurringTaskState;
23 import com.google.ipc.invalidation.util.ExponentialBackoffDelayGenerator;
24 import com.google.ipc.invalidation.util.InternalBase;
25 import com.google.ipc.invalidation.util.Marshallable;
26 import com.google.ipc.invalidation.util.NamedRunnable;
27 import com.google.ipc.invalidation.util.Preconditions;
28 import com.google.ipc.invalidation.util.Smearer;
29 import com.google.ipc.invalidation.util.TextBuilder;
30
31
32 /**
33  * An abstraction for scheduling recurring tasks. Combines idempotent scheduling and smearing with
34  * conditional retries and exponential backoff. Does not implement throttling. Designed to support a
35  * variety of use cases, including:
36  *
37  * <ul>
38  * <li>Idempotent scheduling, e.g., ensuring that a batching task is scheduled exactly once.
39  * <li>Recurring tasks, e.g., periodic heartbeats.
40  * <li>Retriable actions aimed at state change, e.g., sending initialization messages.
41  * </ul>
42  * Each instance of this class manages the state for a single task. Examples:
43  *
44  * <pre>
45  * batchingTask = new RecurringTask("Batching", scheduler, logger, smearer, null,
46  *   batchingDelayMs, NO_DELAY) {
47  *  @Override
48  *  public boolean runTask() {
49  *    throttle.fire();
50  *    return false;  // don't reschedule.
51  *  }
52  * };
53  * heartbeatTask = new RecurringTask("Heartbeat", scheduler, logger, smearer, null,
54  *   heartbeatDelayMs, NO_DELAY) {
55  *  @Override
56  *  public boolean runTask() {
57  *    sendInfoMessageToServer(false, !registrationManager.isStateInSyncWithServer());
58  *    return true;  // reschedule
59  *  }
60  * };
61  * initializeTask = new RecurringTask("Token", scheduler, logger, smearer, expDelayGen, NO_DELAY,
62  *    networkTimeoutMs) {
63  *  @Override
64  *  public boolean runTask() {
65  *   // If token is still not assigned (as expected), sends a request. Otherwise, ignore.
66  *   if (clientToken == null) {
67  *     // Allocate a nonce and send a message requesting a new token.
68  *     setNonce(ByteString.copyFromUtf8(Long.toString(internalScheduler.getCurrentTimeMs())));
69  *     protocolHandler.sendInitializeMessage(applicationClientId, nonce, debugString);
70  *     return true;  // reschedule to check state, retry if necessary after timeout
71  *    } else {
72  *     return false;  // don't reschedule
73  *    }
74  *  }
75  * };
76  *</pre>
77  *
78  */
79 public abstract class RecurringTask extends InternalBase
80     implements Marshallable<RecurringTaskState> {
81
82   /** Name of the task (for debugging purposes mostly). */
83   private final String name;
84
85   /** A logger */
86   private final Logger logger;
87
88   /** Scheduler for the scheduling the task as needed. */
89   private final Scheduler scheduler;
90
91   /**
92    * The time after which the task is scheduled first. If no delayGenerator is specified, this is
93    * also the delay used for retries.
94    */
95   private final int initialDelayMs;
96
97   /** For a task that is retried, add this time to the delay. */
98   private final int timeoutDelayMs;
99
100   /** A smearer for spreading the delays. */
101   private final Smearer smearer;
102
103   /** A delay generator for exponential backoff. */
104   private final TiclExponentialBackoffDelayGenerator delayGenerator;
105
106   /** The runnable that is scheduled for the task. */
107   private final NamedRunnable runnable;
108
109   /** If the task has been currently scheduled. */
110   private boolean isScheduled;
111
112   /**
113    * Creates a recurring task with the given parameters. The specs of the parameters are given in
114    * the instance variables.
115    * <p>
116    * The created task is first scheduled with a smeared delay of {@code initialDelayMs}. If the
117    * {@code this.run()} returns true on its execution, the task is rescheduled after a
118    * {@code timeoutDelayMs} + smeared delay of {@code initialDelayMs} or {@code timeoutDelayMs} +
119    * {@code delayGenerator.getNextDelay()} depending on whether the {@code delayGenerator} is null
120    * or not.
121    */
122   
123   public RecurringTask(String name, Scheduler scheduler, Logger logger, Smearer smearer,
124       TiclExponentialBackoffDelayGenerator delayGenerator,
125       final int initialDelayMs, final int timeoutDelayMs) {
126     this.delayGenerator = delayGenerator;
127     this.name = Preconditions.checkNotNull(name);
128     this.logger = Preconditions.checkNotNull(logger);
129     this.scheduler = Preconditions.checkNotNull(scheduler);
130     this.smearer = Preconditions.checkNotNull(smearer);
131     this.initialDelayMs = initialDelayMs;
132     this.isScheduled = false;
133     this.timeoutDelayMs = timeoutDelayMs;
134
135     // Create a runnable that runs the task. If the task asks for a retry, reschedule it after
136     // at a timeout delay. Otherwise, resets the delayGenerator.
137     this.runnable = createRunnable();
138   }
139
140   /**
141    * Creates a recurring task from {@code marshalledState}. Other parameters are as in the
142    * constructor above.
143    */
144   RecurringTask(String name, Scheduler scheduler, Logger logger, Smearer smearer,
145       TiclExponentialBackoffDelayGenerator delayGenerator,
146       RecurringTaskState marshalledState) {
147     this(name, scheduler, logger, smearer, delayGenerator, marshalledState.getInitialDelayMs(),
148         marshalledState.getTimeoutDelayMs());
149     this.isScheduled = marshalledState.getScheduled();
150   }
151
152   private NamedRunnable createRunnable() {
153     return new NamedRunnable(name) {
154       @Override
155       public void run() {
156         Preconditions.checkState(scheduler.isRunningOnThread(), "Not on scheduler thread");
157         isScheduled = false;
158         if (runTask()) {
159           // The task asked to be rescheduled, so reschedule it after a timeout has occured.
160           Preconditions.checkState((delayGenerator != null) || (initialDelayMs != 0),
161               "Spinning: No exp back off and initialdelay is zero");
162           ensureScheduled(true, "Retry");
163         } else if (delayGenerator != null) {
164           // The task asked not to be rescheduled.  Treat it as having "succeeded" and reset the
165           // delay generator.
166           delayGenerator.reset();
167         }
168       }
169     };
170   }
171
172   /**
173    * Run the task and return true if the task should be rescheduled after a timeout. If false is
174    * returned, the task is not scheduled again until {@code ensureScheduled} is called again.
175    */
176   public abstract boolean runTask();
177
178   /** Returns the smearer used for randomizing delays. */
179   Smearer getSmearer() {
180     return smearer;
181   }
182
183   /** Returns the delay generator, if any. */
184   ExponentialBackoffDelayGenerator getDelayGenerator() {
185     return delayGenerator;
186   }
187
188   /**
189    * Ensures that the task is scheduled (with {@code debugReason} as the reason to be printed
190    * for debugging purposes). If the task has been scheduled, it is not scheduled again.
191    * <p>
192    * REQUIRES: Must be called from the scheduler thread.
193    */
194   
195   public void ensureScheduled(String debugReason) {
196     ensureScheduled(false, debugReason);
197   }
198
199   /**
200    * Ensures that the task is scheduled if it is already not scheduled. If already scheduled, this
201    * method is a no-op.
202    *
203    * @param isRetry If this is {@code false}, smears the {@code initialDelayMs} and uses that delay
204    *        for scheduling. If {@code isRetry} is true, it determines the new delay to be
205    *        {@code timeoutDelayMs} + {@ocde delayGenerator.getNextDelay()} if
206    *        {@code delayGenerator} is non-null. If {@code delayGenerator} is null, schedules the
207    *        task after a delay of {@code timeoutDelayMs} + smeared value of {@code initialDelayMs}
208    * <p>
209    * REQUIRES: Must be called from the scheduler thread.
210    */
211   private void ensureScheduled(boolean isRetry, String debugReason) {
212     Preconditions.checkState(scheduler.isRunningOnThread());
213     if (isScheduled) {
214       return;
215     }
216     final int delayMs;
217
218     if (isRetry) {
219       // For a retried task, determine the delay to be timeout + extra delay (depending on whether
220       // a delay generator was provided or not).
221       if (delayGenerator != null) {
222         delayMs = timeoutDelayMs + delayGenerator.getNextDelay();
223       } else {
224         delayMs = timeoutDelayMs + smearer.getSmearedDelay(initialDelayMs);
225       }
226     } else {
227       delayMs = smearer.getSmearedDelay(initialDelayMs);
228     }
229
230     logger.fine("[%s] Scheduling %s with a delay %s, Now = %s", debugReason, name, delayMs,
231         scheduler.getCurrentTimeMs());
232     scheduler.schedule(delayMs, runnable);
233     isScheduled = true;
234   }
235
236   /** For use only in the Android scheduler. */
237   public NamedRunnable getRunnable() {
238     return runnable;
239   }
240
241   @Override
242   public RecurringTaskState marshal() {
243     ExponentialBackoffState backoffState =
244         (delayGenerator == null) ? null : delayGenerator.marshal();
245     return RecurringTaskState.create(initialDelayMs, timeoutDelayMs, isScheduled, backoffState);
246   }
247
248   @Override
249   public void toCompactString(TextBuilder builder) {
250     builder.append("<RecurringTask: name=").append(name)
251         .append(", initialDelayMs=").append(initialDelayMs)
252         .append(", timeoutDelayMs=").append(timeoutDelayMs)
253         .append(", isScheduled=").append(isScheduled)
254         .append(">");
255   }
256 }