Mercurial > hg > blitz_condensed
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 } |