File: ClockDaemon.java

package info (click to toggle)
concurrent-dfsg 1.3.4-4
  • links: PTS, VCS
  • area: main
  • in suites: buster, jessie, jessie-kfreebsd, squeeze, stretch, wheezy
  • size: 976 kB
  • ctags: 2,018
  • sloc: java: 10,704; xml: 49; makefile: 12
file content (387 lines) | stat: -rw-r--r-- 12,287 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
/*
  File: ClockDaemon.java

  Originally written by Doug Lea and released into the public domain.
  This may be used for any purposes whatsoever without acknowledgment.
  Thanks for the assistance and support of Sun Microsystems Labs,
  and everyone contributing, testing, and using this code.

  History:
  Date       Who                What
  29Aug1998  dl               created initial public version
  17dec1998  dl               null out thread after shutdown
*/

package EDU.oswego.cs.dl.util.concurrent;
import java.util.Comparator;
import java.util.Date;

/**
 * A general-purpose time-based daemon, vaguely similar in functionality
 * to common system-level utilities such as <code>at</code> 
 * (and the associated crond) in Unix.
 * Objects of this class maintain a single thread and a task queue
 * that may be used to execute Runnable commands in any of three modes --
 * absolute (run at a given time), relative (run after a given delay),
 * and periodic (cyclically run with a given delay).
 * <p>
 * All commands are executed by the single background thread. 
 * The thread is not actually started until the first 
 * request is encountered. Also, if the
 * thread is stopped for any reason, one is started upon encountering
 * the next request,  or <code>restart()</code> is invoked. 
 * <p>
 * If you would instead like commands run in their own threads, you can
 * use as arguments Runnable commands that start their own threads
 * (or perhaps wrap within ThreadedExecutors). 
 * <p>
 * You can also use multiple
 * daemon objects, each using a different background thread. However,
 * one of the reasons for using a time daemon is to pool together
 * processing of infrequent tasks using a single background thread.
 * <p>
 * Background threads are created using a ThreadFactory. The
 * default factory does <em>not</em>
 * automatically <code>setDaemon</code> status.
 * <p>
 * The class uses Java timed waits for scheduling. These can vary
 * in precision across platforms, and provide no real-time guarantees
 * about meeting deadlines.
 * <p>[<a href="http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html"> Introduction to this package. </a>]
 **/

public class ClockDaemon extends ThreadFactoryUser  {


  /** tasks are maintained in a standard priority queue **/
  protected final Heap heap_ = new Heap(DefaultChannelCapacity.get());


  protected static class TaskNode implements Comparable {
    final Runnable command;   // The command to run
    final long period;        // The cycle period, or -1 if not periodic
    private long timeToRun_;  // The time to run command

    // Cancellation does not immediately remove node, it just
    // sets up lazy deletion bit, so is thrown away when next 
    // encountered in run loop

    private boolean cancelled_ = false;

    // Access to cancellation status and and run time needs sync 
    // since they can be written and read in different threads

    synchronized void setCancelled() { cancelled_ = true; }
    synchronized boolean getCancelled() { return cancelled_; }

    synchronized void setTimeToRun(long w) { timeToRun_ = w; }
    synchronized long getTimeToRun() { return timeToRun_; }
    
    
    public int compareTo(Object other) {
      long a = getTimeToRun();
      long b = ((TaskNode)(other)).getTimeToRun();
      return (a < b)? -1 : ((a == b)? 0 : 1);
    }

    TaskNode(long w, Runnable c, long p) {
      timeToRun_ = w; command = c; period = p;
    }

    TaskNode(long w, Runnable c) { this(w, c, -1); }
  }


  /** 
   * Execute the given command at the given time.
   * @param date -- the absolute time to run the command, expressed
   * as a java.util.Date.
   * @param command -- the command to run at the given time.
   * @return taskID -- an opaque reference that can be used to cancel execution request
   **/
  public Object executeAt(Date date, Runnable command) {
    TaskNode task = new TaskNode(date.getTime(), command); 
    heap_.insert(task);
    restart();
    return task;
  }

  /** 
   * Excecute the given command after waiting for the given delay.
   * <p>
   * <b>Sample Usage.</b>
   * You can use a ClockDaemon to arrange timeout callbacks to break out
   * of stuck IO. For example (code sketch):
   * <pre>
   * class X {   ...
   * 
   *   ClockDaemon timer = ...
   *   Thread readerThread;
   *   FileInputStream datafile;
   * 
   *   void startReadThread() {
   *     datafile = new FileInputStream("data", ...);
   * 
   *     readerThread = new Thread(new Runnable() {
   *      public void run() {
   *        for(;;) {
   *          // try to gracefully exit before blocking
   *         if (Thread.currentThread().isInterrupted()) {
   *           quietlyWrapUpAndReturn();
   *         }
   *         else {
   *           try {
   *             int c = datafile.read();
   *             if (c == -1) break;
   *             else process(c);
   *           }
   *           catch (IOException ex) {
   *            cleanup();
   *            return;
   *          }
   *       }
   *     } };
   *
   *    readerThread.start();
   *
   *    // establish callback to cancel after 60 seconds
   *    timer.executeAfterDelay(60000, new Runnable() {
   *      readerThread.interrupt();    // try to interrupt thread
   *      datafile.close(); // force thread to lose its input file 
   *    });
   *   } 
   * }
   * </pre>
   * @param millisecondsToDelay -- the number of milliseconds
   * from now to run the command.
   * @param command -- the command to run after the delay.
   * @return taskID -- an opaque reference that can be used to cancel execution request
   **/
  public Object executeAfterDelay(long millisecondsToDelay, Runnable command) {
    long runtime = System.currentTimeMillis() + millisecondsToDelay;
    TaskNode task = new TaskNode(runtime, command);
    heap_.insert(task);
    restart();
    return task;
  }

  /** 
   * Execute the given command every <code>period</code> milliseconds.
   * If <code>startNow</code> is true, execution begins immediately,
   * otherwise, it begins after the first <code>period</code> delay.
   * <p>
   * <b>Sample Usage</b>. Here is one way
   * to update Swing components acting as progress indicators for
   * long-running actions.
   * <pre>
   * class X {
   *   JLabel statusLabel = ...;
   *
   *   int percentComplete = 0;
   *   synchronized int  getPercentComplete() { return percentComplete; }
   *   synchronized void setPercentComplete(int p) { percentComplete = p; }
   *
   *   ClockDaemon cd = ...;
   * 
   *   void startWorking() {
   *     Runnable showPct = new Runnable() {
   *       public void run() {
   *          SwingUtilities.invokeLater(new Runnable() {
   *            public void run() {
   *              statusLabel.setText(getPercentComplete() + "%");
   *            } 
   *          } 
   *       } 
   *     };
   *
   *     final Object updater = cd.executePeriodically(500, showPct, true);
   *
   *     Runnable action = new Runnable() {
   *       public void run() {
   *         for (int i = 0; i < 100; ++i) {
   *           work();
   *           setPercentComplete(i);
   *         }
   *         cd.cancel(updater);
   *       }
   *     };
   *
   *     new Thread(action).start();
   *   }
   * }  
   * </pre>
   * @param period -- the period, in milliseconds. Periods are
   *  measured from start-of-task to the next start-of-task. It is
   * generally a bad idea to use a period that is shorter than 
   * the expected task duration.
   * @param command -- the command to run at each cycle
   * @param startNow -- true if the cycle should start with execution
   * of the task now. Otherwise, the cycle starts with a delay of
   * <code>period</code> milliseconds.
   * @exception IllegalArgumentException if period less than or equal to zero.
   * @return taskID -- an opaque reference that can be used to cancel execution request
   **/
  public Object executePeriodically(long period,
                                    Runnable command, 
                                    boolean startNow) {

    if (period <= 0) throw new IllegalArgumentException();

    long firstTime = System.currentTimeMillis();
    if (!startNow) firstTime += period;

    TaskNode task = new TaskNode(firstTime, command, period); 
    heap_.insert(task);
    restart();
    return task;
  }

  /** 
   * Cancel a scheduled task that has not yet been run. 
   * The task will be cancelled
   * upon the <em>next</em> opportunity to run it. This has no effect if
   * this is a one-shot task that has already executed.
   * Also, if an execution is in progress, it will complete normally.
   * (It may however be interrupted via getThread().interrupt()).
   * But if it is a periodic task, future iterations are cancelled. 
   * @param taskID -- a task reference returned by one of
   * the execute commands
   * @exception ClassCastException if the taskID argument is not 
   * of the type returned by an execute command.
   **/
  public static void cancel(Object taskID) {
    ((TaskNode)taskID).setCancelled();
  }
   

  /** The thread used to process commands **/
  protected Thread thread_;

  
  /**
   * Return the thread being used to process commands, or
   * null if there is no such thread. You can use this
   * to invoke any special methods on the thread, for
   * example, to interrupt it.
   **/
  public synchronized Thread getThread() { 
    return thread_;
  }

  /** set thread_ to null to indicate termination **/
  protected synchronized void clearThread() {
    thread_ = null;
  }

  /**
   * Start (or restart) a thread to process commands, or wake
   * up an existing thread if one is already running. This
   * method can be invoked if the background thread crashed
   * due to an unrecoverable exception in an executed command.
   **/

  public synchronized void restart() {
    if (thread_ == null) {
      thread_ = threadFactory_.newThread(runLoop_);
      thread_.start();
    }
    else
      notify();
  }


  /**
   * Cancel all tasks and interrupt the background thread executing
   * the current task, if any.
   * A new background thread will be started if new execution
   * requests are encountered. If the currently executing task
   * does not repsond to interrupts, the current thread may persist, even
   * if a new thread is started via restart().
   **/
  public synchronized void shutDown() {
    heap_.clear();
    if (thread_ != null) 
      thread_.interrupt();
    thread_ = null;
  }

  /** Return the next task to execute, or null if thread is interrupted **/
  protected synchronized TaskNode nextTask() {

    // Note: This code assumes that there is only one run loop thread

    try {
      while (!Thread.interrupted()) {

        // Using peek simplifies dealing with spurious wakeups

        TaskNode task = (TaskNode)(heap_.peek());

        if (task == null) {
          wait();
        }
        else  {
          long now = System.currentTimeMillis();
          long when = task.getTimeToRun();

          if (when > now) { // false alarm wakeup
            wait(when - now);
          }
          else {
            task = (TaskNode)(heap_.extract());

            if (!task.getCancelled()) { // Skip if cancelled by

              if (task.period > 0) {  // If periodic, requeue 
                task.setTimeToRun(now + task.period);
                heap_.insert(task);
              }
              
              return task;
            }
          }
        }
      }
    }
    catch (InterruptedException ex) {  } // fall through

    return null; // on interrupt
  }

  /**
   * The runloop is isolated in its own Runnable class
   * just so that the main 
   * class need not implement Runnable,  which would
   * allow others to directly invoke run, which is not supported.
   **/

  protected class RunLoop implements Runnable {
    public void run() {
      try {
        for (;;) {
          TaskNode task = nextTask();
          if (task != null) 
            task.command.run();
          else
            break;
        }
      }
      finally {
        clearThread();
      }
    }
  }

  protected final RunLoop runLoop_;

  /** 
   * Create a new ClockDaemon 
   **/

  public ClockDaemon() {
    runLoop_ = new RunLoop();
  }

    

}