comparison src/EDU/oswego/cs/dl/util/concurrent/ClockDaemon.java @ 0:3dc0c5604566

Initial checkin of blitz 2.0 fcs - no installer yet.
author Dan Creswell <dan.creswell@gmail.com>
date Sat, 21 Mar 2009 11:00:06 +0000
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:3dc0c5604566
1 /*
2 File: ClockDaemon.java
3
4 Originally written by Doug Lea and released into the public domain.
5 This may be used for any purposes whatsoever without acknowledgment.
6 Thanks for the assistance and support of Sun Microsystems Labs,
7 and everyone contributing, testing, and using this code.
8
9 History:
10 Date Who What
11 29Aug1998 dl created initial public version
12 17dec1998 dl null out thread after shutdown
13 */
14
15 package EDU.oswego.cs.dl.util.concurrent;
16 import java.util.Comparator;
17 import java.util.Date;
18
19 /**
20 * A general-purpose time-based daemon, vaguely similar in functionality
21 * to common system-level utilities such as <code>at</code>
22 * (and the associated crond) in Unix.
23 * Objects of this class maintain a single thread and a task queue
24 * that may be used to execute Runnable commands in any of three modes --
25 * absolute (run at a given time), relative (run after a given delay),
26 * and periodic (cyclically run with a given delay).
27 * <p>
28 * All commands are executed by the single background thread.
29 * The thread is not actually started until the first
30 * request is encountered. Also, if the
31 * thread is stopped for any reason, one is started upon encountering
32 * the next request, or <code>restart()</code> is invoked.
33 * <p>
34 * If you would instead like commands run in their own threads, you can
35 * use as arguments Runnable commands that start their own threads
36 * (or perhaps wrap within ThreadedExecutors).
37 * <p>
38 * You can also use multiple
39 * daemon objects, each using a different background thread. However,
40 * one of the reasons for using a time daemon is to pool together
41 * processing of infrequent tasks using a single background thread.
42 * <p>
43 * Background threads are created using a ThreadFactory. The
44 * default factory does <em>not</em>
45 * automatically <code>setDaemon</code> status.
46 * <p>
47 * The class uses Java timed waits for scheduling. These can vary
48 * in precision across platforms, and provide no real-time guarantees
49 * about meeting deadlines.
50 * <p>[<a href="http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html"> Introduction to this package. </a>]
51 **/
52
53 public class ClockDaemon extends ThreadFactoryUser {
54
55
56 /** tasks are maintained in a standard priority queue **/
57 protected final Heap heap_ = new Heap(DefaultChannelCapacity.get());
58
59
60 protected static class TaskNode implements Comparable {
61 final Runnable command; // The command to run
62 final long period; // The cycle period, or -1 if not periodic
63 private long timeToRun_; // The time to run command
64
65 // Cancellation does not immediately remove node, it just
66 // sets up lazy deletion bit, so is thrown away when next
67 // encountered in run loop
68
69 private boolean cancelled_ = false;
70
71 // Access to cancellation status and and run time needs sync
72 // since they can be written and read in different threads
73
74 synchronized void setCancelled() { cancelled_ = true; }
75 synchronized boolean getCancelled() { return cancelled_; }
76
77 synchronized void setTimeToRun(long w) { timeToRun_ = w; }
78 synchronized long getTimeToRun() { return timeToRun_; }
79
80
81 public int compareTo(Object other) {
82 long a = getTimeToRun();
83 long b = ((TaskNode)(other)).getTimeToRun();
84 return (a < b)? -1 : ((a == b)? 0 : 1);
85 }
86
87 TaskNode(long w, Runnable c, long p) {
88 timeToRun_ = w; command = c; period = p;
89 }
90
91 TaskNode(long w, Runnable c) { this(w, c, -1); }
92 }
93
94
95 /**
96 * Execute the given command at the given time.
97 * @param date -- the absolute time to run the command, expressed
98 * as a java.util.Date.
99 * @param command -- the command to run at the given time.
100 * @return taskID -- an opaque reference that can be used to cancel execution request
101 **/
102 public Object executeAt(Date date, Runnable command) {
103 TaskNode task = new TaskNode(date.getTime(), command);
104 heap_.insert(task);
105 restart();
106 return task;
107 }
108
109 /**
110 * Excecute the given command after waiting for the given delay.
111 * <p>
112 * <b>Sample Usage.</b>
113 * You can use a ClockDaemon to arrange timeout callbacks to break out
114 * of stuck IO. For example (code sketch):
115 * <pre>
116 * class X { ...
117 *
118 * ClockDaemon timer = ...
119 * Thread readerThread;
120 * FileInputStream datafile;
121 *
122 * void startReadThread() {
123 * datafile = new FileInputStream("data", ...);
124 *
125 * readerThread = new Thread(new Runnable() {
126 * public void run() {
127 * for(;;) {
128 * // try to gracefully exit before blocking
129 * if (Thread.currentThread().isInterrupted()) {
130 * quietlyWrapUpAndReturn();
131 * }
132 * else {
133 * try {
134 * int c = datafile.read();
135 * if (c == -1) break;
136 * else process(c);
137 * }
138 * catch (IOException ex) {
139 * cleanup();
140 * return;
141 * }
142 * }
143 * } };
144 *
145 * readerThread.start();
146 *
147 * // establish callback to cancel after 60 seconds
148 * timer.executeAfterDelay(60000, new Runnable() {
149 * readerThread.interrupt(); // try to interrupt thread
150 * datafile.close(); // force thread to lose its input file
151 * });
152 * }
153 * }
154 * </pre>
155 * @param millisecondsToDelay -- the number of milliseconds
156 * from now to run the command.
157 * @param command -- the command to run after the delay.
158 * @return taskID -- an opaque reference that can be used to cancel execution request
159 **/
160 public Object executeAfterDelay(long millisecondsToDelay, Runnable command) {
161 long runtime = System.currentTimeMillis() + millisecondsToDelay;
162 TaskNode task = new TaskNode(runtime, command);
163 heap_.insert(task);
164 restart();
165 return task;
166 }
167
168 /**
169 * Execute the given command every <code>period</code> milliseconds.
170 * If <code>startNow</code> is true, execution begins immediately,
171 * otherwise, it begins after the first <code>period</code> delay.
172 * <p>
173 * <b>Sample Usage</b>. Here is one way
174 * to update Swing components acting as progress indicators for
175 * long-running actions.
176 * <pre>
177 * class X {
178 * JLabel statusLabel = ...;
179 *
180 * int percentComplete = 0;
181 * synchronized int getPercentComplete() { return percentComplete; }
182 * synchronized void setPercentComplete(int p) { percentComplete = p; }
183 *
184 * ClockDaemon cd = ...;
185 *
186 * void startWorking() {
187 * Runnable showPct = new Runnable() {
188 * public void run() {
189 * SwingUtilities.invokeLater(new Runnable() {
190 * public void run() {
191 * statusLabel.setText(getPercentComplete() + "%");
192 * }
193 * }
194 * }
195 * };
196 *
197 * final Object updater = cd.executePeriodically(500, showPct, true);
198 *
199 * Runnable action = new Runnable() {
200 * public void run() {
201 * for (int i = 0; i < 100; ++i) {
202 * work();
203 * setPercentComplete(i);
204 * }
205 * cd.cancel(updater);
206 * }
207 * };
208 *
209 * new Thread(action).start();
210 * }
211 * }
212 * </pre>
213 * @param period -- the period, in milliseconds. Periods are
214 * measured from start-of-task to the next start-of-task. It is
215 * generally a bad idea to use a period that is shorter than
216 * the expected task duration.
217 * @param command -- the command to run at each cycle
218 * @param startNow -- true if the cycle should start with execution
219 * of the task now. Otherwise, the cycle starts with a delay of
220 * <code>period</code> milliseconds.
221 * @exception IllegalArgumentException if period less than or equal to zero.
222 * @return taskID -- an opaque reference that can be used to cancel execution request
223 **/
224 public Object executePeriodically(long period,
225 Runnable command,
226 boolean startNow) {
227
228 if (period <= 0) throw new IllegalArgumentException();
229
230 long firstTime = System.currentTimeMillis();
231 if (!startNow) firstTime += period;
232
233 TaskNode task = new TaskNode(firstTime, command, period);
234 heap_.insert(task);
235 restart();
236 return task;
237 }
238
239 /**
240 * Cancel a scheduled task that has not yet been run.
241 * The task will be cancelled
242 * upon the <em>next</em> opportunity to run it. This has no effect if
243 * this is a one-shot task that has already executed.
244 * Also, if an execution is in progress, it will complete normally.
245 * (It may however be interrupted via getThread().interrupt()).
246 * But if it is a periodic task, future iterations are cancelled.
247 * @param taskID -- a task reference returned by one of
248 * the execute commands
249 * @exception ClassCastException if the taskID argument is not
250 * of the type returned by an execute command.
251 **/
252 public static void cancel(Object taskID) {
253 ((TaskNode)taskID).setCancelled();
254 }
255
256
257 /** The thread used to process commands **/
258 protected Thread thread_;
259
260
261 /**
262 * Return the thread being used to process commands, or
263 * null if there is no such thread. You can use this
264 * to invoke any special methods on the thread, for
265 * example, to interrupt it.
266 **/
267 public synchronized Thread getThread() {
268 return thread_;
269 }
270
271 /** set thread_ to null to indicate termination **/
272 protected synchronized void clearThread() {
273 thread_ = null;
274 }
275
276 /**
277 * Start (or restart) a thread to process commands, or wake
278 * up an existing thread if one is already running. This
279 * method can be invoked if the background thread crashed
280 * due to an unrecoverable exception in an executed command.
281 **/
282
283 public synchronized void restart() {
284 if (thread_ == null) {
285 thread_ = threadFactory_.newThread(runLoop_);
286 thread_.start();
287 }
288 else
289 notify();
290 }
291
292
293 /**
294 * Cancel all tasks and interrupt the background thread executing
295 * the current task, if any.
296 * A new background thread will be started if new execution
297 * requests are encountered. If the currently executing task
298 * does not repsond to interrupts, the current thread may persist, even
299 * if a new thread is started via restart().
300 **/
301 public synchronized void shutDown() {
302 heap_.clear();
303 if (thread_ != null)
304 thread_.interrupt();
305 thread_ = null;
306 }
307
308 /** Return the next task to execute, or null if thread is interrupted **/
309 protected synchronized TaskNode nextTask() {
310
311 // Note: This code assumes that there is only one run loop thread
312
313 try {
314 while (!Thread.interrupted()) {
315
316 // Using peek simplifies dealing with spurious wakeups
317
318 TaskNode task = (TaskNode)(heap_.peek());
319
320 if (task == null) {
321 wait();
322 }
323 else {
324 long now = System.currentTimeMillis();
325 long when = task.getTimeToRun();
326
327 if (when > now) { // false alarm wakeup
328 wait(when - now);
329 }
330 else {
331 task = (TaskNode)(heap_.extract());
332
333 if (!task.getCancelled()) { // Skip if cancelled by
334
335 if (task.period > 0) { // If periodic, requeue
336 task.setTimeToRun(now + task.period);
337 heap_.insert(task);
338 }
339
340 return task;
341 }
342 }
343 }
344 }
345 }
346 catch (InterruptedException ex) { } // fall through
347
348 return null; // on interrupt
349 }
350
351 /**
352 * The runloop is isolated in its own Runnable class
353 * just so that the main
354 * class need not implement Runnable, which would
355 * allow others to directly invoke run, which is not supported.
356 **/
357
358 protected class RunLoop implements Runnable {
359 public void run() {
360 try {
361 for (;;) {
362 TaskNode task = nextTask();
363 if (task != null)
364 task.command.run();
365 else
366 break;
367 }
368 }
369 finally {
370 clearThread();
371 }
372 }
373 }
374
375 protected final RunLoop runLoop_;
376
377 /**
378 * Create a new ClockDaemon
379 **/
380
381 public ClockDaemon() {
382 runLoop_ = new RunLoop();
383 }
384
385
386
387 }