KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > EDU > oswego > cs > dl > util > concurrent > ClockDaemon


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 JavaDoc;
17 import java.util.Date JavaDoc;
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 JavaDoc {
61     final Runnable JavaDoc 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 JavaDoc 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 JavaDoc c, long p) {
88       timeToRun_ = w; command = c; period = p;
89     }
90
91     TaskNode(long w, Runnable JavaDoc 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 JavaDoc executeAt(Date JavaDoc date, Runnable JavaDoc 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 JavaDoc executeAfterDelay(long millisecondsToDelay, Runnable JavaDoc 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 JavaDoc executePeriodically(long period,
225                                     Runnable JavaDoc command,
226                                     boolean startNow) {
227
228     if (period <= 0) throw new IllegalArgumentException JavaDoc();
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 JavaDoc taskID) {
253     ((TaskNode)taskID).setCancelled();
254   }
255    
256
257   /** The thread used to process commands **/
258   protected Thread JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc {
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 }
388
Popular Tags