1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase;
20
21 import java.util.concurrent.ScheduledThreadPoolExecutor;
22 import java.util.concurrent.TimeUnit;
23
24 import org.apache.commons.logging.Log;
25 import org.apache.commons.logging.LogFactory;
26 import org.apache.hadoop.hbase.classification.InterfaceAudience;
27
28 import com.google.common.annotations.VisibleForTesting;
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 @InterfaceAudience.Private
44 public abstract class ScheduledChore implements Runnable {
45 private final Log LOG = LogFactory.getLog(this.getClass());
46
47 private final String name;
48
49
50
51
52 private final static TimeUnit DEFAULT_TIME_UNIT = TimeUnit.MILLISECONDS;
53 private final static long DEFAULT_INITIAL_DELAY = 0;
54
55
56
57
58 private final int period;
59 private final TimeUnit timeUnit;
60 private final long initialDelay;
61
62
63
64
65
66 private ChoreServicer choreServicer;
67
68
69
70
71 private long timeOfLastRun = -1;
72 private long timeOfThisRun = -1;
73 private boolean initialChoreComplete = false;
74
75
76
77
78
79
80
81 private final Stoppable stopper;
82
83 interface ChoreServicer {
84
85
86
87 public void cancelChore(ScheduledChore chore);
88 public void cancelChore(ScheduledChore chore, boolean mayInterruptIfRunning);
89
90
91
92
93 public boolean isChoreScheduled(ScheduledChore chore);
94
95
96
97
98
99
100
101
102 public boolean triggerNow(ScheduledChore chore);
103
104
105
106
107
108
109
110
111
112
113 public void onChoreMissedStartTime(ScheduledChore chore);
114 }
115
116
117
118
119 protected ScheduledChore() {
120 this.name = null;
121 this.stopper = null;
122 this.period = 0;
123 this.initialDelay = DEFAULT_INITIAL_DELAY;
124 this.timeUnit = DEFAULT_TIME_UNIT;
125 }
126
127
128
129
130
131
132 public ScheduledChore(final String name, Stoppable stopper, final int period) {
133 this(name, stopper, period, DEFAULT_INITIAL_DELAY);
134 }
135
136
137
138
139
140
141
142
143
144 public ScheduledChore(final String name, Stoppable stopper, final int period,
145 final long initialDelay) {
146 this(name, stopper, period, initialDelay, DEFAULT_TIME_UNIT);
147 }
148
149
150
151
152
153
154
155
156
157
158 public ScheduledChore(final String name, Stoppable stopper, final int period,
159 final long initialDelay, final TimeUnit unit) {
160 this.name = name;
161 this.stopper = stopper;
162 this.period = period;
163 this.initialDelay = initialDelay < 0 ? 0 : initialDelay;
164 this.timeUnit = unit;
165 }
166
167
168
169
170 @Override
171 public void run() {
172 updateTimeTrackingBeforeRun();
173 if (missedStartTime() && isScheduled()) {
174 onChoreMissedStartTime();
175 if (LOG.isInfoEnabled()) LOG.info("Chore: " + getName() + " missed its start time");
176 } else if (stopper.isStopped() || !isScheduled()) {
177 cancel(false);
178 cleanup();
179 if (LOG.isInfoEnabled()) LOG.info("Chore: " + getName() + " was stopped");
180 } else {
181 try {
182 if (!initialChoreComplete) {
183 initialChoreComplete = initialChore();
184 } else {
185 chore();
186 }
187 } catch (Throwable t) {
188 if (LOG.isErrorEnabled()) LOG.error("Caught error", t);
189 if (this.stopper.isStopped()) {
190 cancel(false);
191 cleanup();
192 }
193 }
194 }
195 }
196
197
198
199
200
201 private synchronized void updateTimeTrackingBeforeRun() {
202 timeOfLastRun = timeOfThisRun;
203 timeOfThisRun = System.currentTimeMillis();
204 }
205
206
207
208
209
210
211 private synchronized void onChoreMissedStartTime() {
212 if (choreServicer != null) choreServicer.onChoreMissedStartTime(this);
213 }
214
215
216
217
218
219 synchronized long getTimeBetweenRuns() {
220 return timeOfThisRun - timeOfLastRun;
221 }
222
223
224
225
226 private synchronized boolean missedStartTime() {
227 return isValidTime(timeOfLastRun) && isValidTime(timeOfThisRun)
228 && getTimeBetweenRuns() > getMaximumAllowedTimeBetweenRuns();
229 }
230
231 private synchronized double getMaximumAllowedTimeBetweenRuns() {
232
233 return 1.5 * timeUnit.toMillis(period);
234 }
235
236 private synchronized boolean isValidTime(final long time) {
237 return time > 0 && time <= System.currentTimeMillis();
238 }
239
240
241
242
243 public synchronized boolean triggerNow() {
244 if (choreServicer != null) {
245 return choreServicer.triggerNow(this);
246 } else {
247 return false;
248 }
249 }
250
251 synchronized void setChoreServicer(ChoreServicer service) {
252
253
254 if (choreServicer != null && choreServicer != service) {
255 choreServicer.cancelChore(this, false);
256 }
257 choreServicer = service;
258 timeOfThisRun = System.currentTimeMillis();
259 }
260
261 public synchronized void cancel() {
262 cancel(true);
263 }
264
265 public synchronized void cancel(boolean mayInterruptIfRunning) {
266 if (isScheduled()) choreServicer.cancelChore(this, mayInterruptIfRunning);
267
268 choreServicer = null;
269 }
270
271 public synchronized String getName() {
272 return name;
273 }
274
275 public synchronized Stoppable getStopper() {
276 return stopper;
277 }
278
279 public synchronized int getPeriod() {
280 return period;
281 }
282
283 public synchronized long getInitialDelay() {
284 return initialDelay;
285 }
286
287 public final synchronized TimeUnit getTimeUnit() {
288 return timeUnit;
289 }
290
291 public synchronized boolean isInitialChoreComplete() {
292 return initialChoreComplete;
293 }
294
295 @VisibleForTesting
296 synchronized ChoreServicer getChoreServicer() {
297 return choreServicer;
298 }
299
300 @VisibleForTesting
301 synchronized long getTimeOfLastRun() {
302 return timeOfLastRun;
303 }
304
305 @VisibleForTesting
306 synchronized long getTimeOfThisRun() {
307 return timeOfThisRun;
308 }
309
310
311
312
313 public synchronized boolean isScheduled() {
314 return choreServicer != null && choreServicer.isChoreScheduled(this);
315 }
316
317 @VisibleForTesting
318 public synchronized void choreForTesting() {
319 chore();
320 }
321
322
323
324
325 protected abstract void chore();
326
327
328
329
330
331 protected boolean initialChore() {
332
333 return true;
334 }
335
336
337
338
339 protected synchronized void cleanup() {
340 }
341
342 @Override
343 public String toString() {
344 return "[ScheduledChore: Name: " + getName() + " Period: " + getPeriod() + " Unit: "
345 + getTimeUnit() + "]";
346 }
347 }