KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > util > Alarm


1 /*
2  * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
3  *
4  * This file is part of Resin(R) Open Source
5  *
6  * Each copy or derived work must preserve the copyright notice and this
7  * notice unmodified.
8  *
9  * Resin Open Source is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Resin Open Source is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
17  * of NON-INFRINGEMENT. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Resin Open Source; if not, write to the
22  *
23  * Free Software Foundation, Inc.
24  * 59 Temple Place, Suite 330
25  * Boston, MA 02111-1307 USA
26  *
27  * @author Scott Ferguson
28  */

29
30 package com.caucho.util;
31
32 import com.caucho.log.Log;
33
34 import java.lang.reflect.Method JavaDoc;
35 import java.util.logging.Level JavaDoc;
36 import java.util.logging.Logger JavaDoc;
37
38 /**
39  * The alarm class provides a lightweight event scheduler. This allows
40  * an objects to schedule a timeout without creating a new thread.
41  *
42  * <p>A separate thread periodically tests the queue for alarms ready.
43  */

44 public class Alarm implements ThreadTask {
45   static private final Logger JavaDoc log = Log.open(Alarm.class);
46   static private final Integer JavaDoc timeLock = new Integer JavaDoc(0);
47
48   static private volatile long _currentTime = System.currentTimeMillis();
49
50   static private int _concurrentAlarmThrottle = 5;
51   
52   static private Object JavaDoc _queueLock = new Object JavaDoc();
53   
54   static private AlarmThread _alarmThread;
55
56   static private Alarm []_heap = new Alarm[256];
57   static private int _heapTop;
58   
59   static private volatile int _runningAlarmCount;
60   
61   static private long _testTime;
62   static private int _testCount;
63
64   static private final Method JavaDoc _nanoTimeMethod;
65
66   private long _wakeTime;
67   private AlarmListener _listener;
68   private ClassLoader JavaDoc _contextLoader;
69   private String JavaDoc _name;
70   
71   private int _heapIndex = 0;
72
73   private volatile boolean _isRunning;
74
75   static {
76     _currentTime = System.currentTimeMillis();
77     _alarmThread = new AlarmThread();
78     _alarmThread.start();
79
80     Method JavaDoc nanoTimeMethod;
81
82     try
83     {
84       nanoTimeMethod = System JavaDoc.class.getMethod("nanoTime", null);
85     }
86     catch (NoSuchMethodException JavaDoc e)
87     {
88       nanoTimeMethod = null;
89     }
90
91     _nanoTimeMethod = nanoTimeMethod;
92   }
93     
94   /**
95    * Create a new wakeup alarm with a designated listener as a callback.
96    * The alarm is not scheduled.
97    */

98   protected Alarm()
99   {
100     _name = "alarm";
101   }
102     
103   /**
104    * Create a new wakeup alarm with a designated listener as a callback.
105    * The alarm is not scheduled.
106    */

107   public Alarm(AlarmListener listener)
108   {
109     this("alarm[" + listener + "]", listener);
110   }
111     
112   /**
113    * Create a new wakeup alarm with a designated listener as a callback.
114    * The alarm is not scheduled.
115    */

116   public Alarm(String JavaDoc name, AlarmListener listener)
117   {
118     this(name, listener, Thread.currentThread().getContextClassLoader());
119   }
120     
121   /**
122    * Create a new wakeup alarm with a designated listener as a callback.
123    * The alarm is not scheduled.
124    */

125   public Alarm(String JavaDoc name, AlarmListener listener, ClassLoader JavaDoc loader)
126   {
127     _name = name;
128     
129     setListener(listener);
130     setContextLoader(loader);
131   }
132
133   /**
134    * Creates a named alarm and schedules its wakeup.
135    *
136    * @param name the object prepared to receive the callback
137    * @param listener the object prepared to receive the callback
138    * @param delta the time in milliseconds to wake up
139    */

140   public Alarm(String JavaDoc name, AlarmListener listener, long delta)
141   {
142     this(listener);
143
144     _name = name;
145     queue(delta);
146   }
147   /**
148    * Creates a new alarm and schedules its wakeup.
149    *
150    * @param listener the object prepared to receive the callback
151    * @param delta the time in milliseconds to wake up
152    */

153   public Alarm(AlarmListener listener, long delta)
154   {
155     this(listener);
156
157     queue(delta);
158   }
159
160   /**
161    * Returns the alarm name.
162    */

163   public String JavaDoc getName()
164   {
165     return _name;
166   }
167
168   /**
169    * Sets the alarm name.
170    */

171   protected void setName(String JavaDoc name)
172   {
173     _name = name;
174   }
175
176   /**
177    * Returns the approximate current time in milliseconds.
178    * Convenient for minimizing system calls.
179    */

180   public static long getCurrentTime()
181   {
182     return _currentTime;
183   }
184
185   /**
186    * Returns the exact current time in milliseconds.
187    */

188   public static long getExactTime()
189   {
190     if (_testTime > 0)
191       return _testTime;
192     else
193       return System.currentTimeMillis();
194   }
195
196   /**
197    * Returns the exact current time in nanoseconds.
198    */

199   public static long getExactTimeNanoseconds()
200   {
201     if (_testTime > 0)
202       return _testTime * 1000000L;
203
204     return getExactTime() * 1000000L;
205   }
206
207   /**
208    * Returns true for testing.
209    */

210   public static boolean isTest()
211   {
212     return _testTime > 0;
213   }
214   
215   /**
216    * Returns the wake time of this alarm.
217    */

218   public long getWakeTime()
219   {
220     return _wakeTime;
221   }
222   
223   /**
224    * Return the alarm's listener.
225    */

226   public AlarmListener getListener()
227   {
228     return _listener;
229   }
230   
231   /**
232    * Sets the alarm's listener.
233    */

234   public void setListener(AlarmListener listener)
235   {
236     _listener = listener;
237   }
238   
239   /**
240    * Sets the alarm's context loader
241    */

242   public void setContextLoader(ClassLoader JavaDoc loader)
243   {
244     _contextLoader = loader;
245   }
246   
247   /**
248    * Sets the alarm's context loader
249    */

250   public ClassLoader JavaDoc getContextLoader()
251   {
252     return _contextLoader;
253   }
254
255   /**
256    * Returns true if the alarm is currently queued.
257    */

258   public boolean isQueued()
259   {
260     return _heapIndex != 0;
261   }
262
263   /**
264    * Returns true if the alarm is currently running
265    */

266   boolean isRunning()
267   {
268     return _isRunning;
269   }
270
271   /**
272    * Queue the alarm for wakeup.
273    *
274    * @param delta time in milliseconds to wake
275    */

276   public void queue(long delta)
277   {
278     synchronized (_queueLock) {
279       if (_heapIndex > 0)
280     dequeueImpl(this);
281
282       long wakeTime = delta + getCurrentTime();
283       _wakeTime = wakeTime;
284
285       insertImpl(this);
286     }
287   }
288
289   /**
290    * Remove the alarm from the wakeup queue.
291    */

292   public void dequeue()
293   {
294     synchronized (_queueLock) {
295       if (_heapIndex > 0)
296     dequeueImpl(this);
297     }
298   }
299
300   /**
301    * Runs the alarm. This is only called from the worker thread.
302    */

303   public void run()
304   {
305     try {
306       handleAlarm();
307     } catch (Throwable JavaDoc e) {
308       log.log(Level.WARNING, e.toString(), e);
309     } finally {
310       synchronized (_queueLock) {
311     _isRunning = false;
312     _runningAlarmCount--;
313       }
314     }
315   }
316
317   /**
318    * Handles the alarm.
319    */

320   private void handleAlarm()
321   {
322     AlarmListener listener = getListener();
323     
324     if (listener == null)
325       return;
326     
327     Thread JavaDoc thread = Thread.currentThread();
328     ClassLoader JavaDoc loader = getContextLoader();
329     
330     if (loader != null)
331       thread.setContextClassLoader(loader);
332     else
333       thread.setContextClassLoader(ClassLoader.getSystemClassLoader());
334
335     try {
336       listener.handleAlarm(this);
337     } finally {
338       thread.setContextClassLoader(ClassLoader.getSystemClassLoader());
339     }
340   }
341
342   /**
343    * Closes the alarm instance
344    */

345   public void close()
346   {
347     dequeue();
348     _listener = null;
349     _contextLoader = null;
350   }
351
352   /**
353    * Returns the next alarm ready to run
354    */

355   static Alarm extractAlarm()
356   {
357     long now = getCurrentTime();
358
359     synchronized (_queueLock) {
360       Alarm []heap = _heap;
361
362       Alarm alarm = heap[1];
363
364       if (alarm == null)
365     return null;
366       else if (now < alarm._wakeTime)
367     return null;
368
369       dequeueImpl(alarm);
370
371       return alarm;
372     }
373   }
374
375   /**
376    * Removes the alarm item. Must be called from within the heap lock.
377    */

378   private static void insertImpl(Alarm item)
379   {
380     if (item._heapIndex != 0)
381       throw new IllegalStateException JavaDoc();
382     
383     // resize if necessary
384
if (_heap.length <= _heapTop + 2) {
385       Alarm []newHeap = new Alarm[2 * _heap.length];
386       System.arraycopy(_heap, 0, newHeap, 0, _heap.length);
387       _heap = newHeap;
388     }
389
390     Alarm []heap = _heap;
391
392     int i = ++_heapTop;
393     int parent;
394     Alarm alarm;
395     long wakeTime = item._wakeTime;
396
397     while (i > 1 && wakeTime < (alarm = heap[parent = (i >> 1)])._wakeTime) {
398       heap[i] = alarm;
399       alarm._heapIndex = i;
400       i = parent;
401     }
402
403     heap[i] = item;
404     item._heapIndex = i;
405
406     if (_heapTop < i)
407       throw new IllegalStateException JavaDoc();
408   }
409
410   /**
411    * Removes the alarm item. Must be called from within the heap lock.
412    */

413   private static void dequeueImpl(Alarm item)
414   {
415     int i = item._heapIndex;
416
417     if (i < 1)
418       return;
419     
420     if (_heapTop < i)
421       throw new IllegalStateException JavaDoc("bad heap: " + _heapTop + " index:" + i);
422
423     Alarm []heap = _heap;
424
425     if (_heapTop < 1)
426       throw new IllegalStateException JavaDoc();
427
428     int size = _heapTop--;
429     
430     heap[i] = heap[size];
431     heap[i]._heapIndex = i;
432     heap[size] = null;
433     
434     item._heapIndex = 0;
435
436     if (size == i)
437       return;
438
439     if (item._wakeTime < heap[i]._wakeTime) {
440       while (i < size) {
441     item = heap[i];
442
443     int minIndex = i;
444     long minWakeTime = item._wakeTime;
445       
446     int left = i << 1;
447     if (left < size && heap[left]._wakeTime < minWakeTime) {
448       minIndex = left;
449       minWakeTime = heap[left]._wakeTime;
450     }
451       
452     int right = left + 1;
453     if (right < size && heap[right]._wakeTime < minWakeTime)
454       minIndex = right;
455
456     if (i == minIndex)
457       return;
458
459     heap[i] = heap[minIndex];
460     heap[i]._heapIndex = i;
461     heap[minIndex] = item;
462     item._heapIndex = minIndex;
463       
464     i = minIndex;
465       }
466     }
467     else {
468       int parent;
469       Alarm alarm;
470       item = heap[i];
471       long wakeTime = item._wakeTime;
472
473       while (i > 1 && wakeTime < (alarm = heap[parent = (i >> 1)])._wakeTime) {
474     heap[i] = alarm;
475     alarm._heapIndex = i;
476     i = parent;
477       }
478
479       heap[i] = item;
480       item._heapIndex = i;
481     }
482   }
483
484   // test
485

486   static void testClear()
487   {
488     for (; _heapTop > 0; _heapTop--) {
489       Alarm alarm = _heap[_heapTop];
490       alarm._heapIndex = 0;
491       _heap[_heapTop] = null;
492     }
493   }
494   
495   static void setTestTime(long time)
496   {
497     _testTime = time;
498     
499     if (_testTime > 0) {
500       if (time < _currentTime) {
501     testClear();
502       }
503
504       _currentTime = time;
505     }
506     else
507       _currentTime = System.currentTimeMillis();
508
509     Alarm alarm;
510
511     Thread JavaDoc thread = Thread.currentThread();
512     ClassLoader JavaDoc oldLoader = thread.getContextClassLoader();
513
514     try {
515       while ((alarm = Alarm.extractAlarm()) != null) {
516     alarm.run();
517       }
518     } finally {
519       thread.setContextClassLoader(oldLoader);
520     }
521     
522     try {
523       Thread.currentThread().sleep(10);
524     } catch (Exception JavaDoc e) {
525     }
526   }
527
528   public String JavaDoc toString()
529   {
530     return "Alarm[" + _name + "]";
531   }
532
533   static class AlarmThread extends Thread JavaDoc {
534     private CoordinatorTask _coordinator = new CoordinatorTask();
535
536     public void run()
537     {
538       while (true) {
539     try {
540       if (_testTime > 0)
541         _currentTime = _testTime;
542       else
543         _currentTime = System.currentTimeMillis();
544
545       _coordinator.schedule();
546     
547       Thread.sleep(500);
548     } catch (Throwable JavaDoc e) {
549     }
550       }
551     }
552
553     AlarmThread()
554     {
555       super("resin-alarm");
556       setDaemon(true);
557     }
558   }
559
560   private static class CoordinatorTask implements ThreadTask {
561     private boolean _isRunning;
562     
563     /**
564      * schedules the task.
565      */

566     void schedule()
567     {
568       boolean isRunning;
569
570       synchronized (this) {
571     isRunning = _isRunning;
572     _isRunning = true;
573       }
574       
575       if (! isRunning)
576     ThreadPool.getThreadPool().schedulePriority(this);
577     }
578     
579     /**
580      * Runs the coordinator task.
581      */

582     public void run()
583     {
584       try {
585     Thread JavaDoc thread = Thread.currentThread();
586     // String oldName = thread.getName();
587

588     // thread.setName("alarm-coordinator");
589

590     Alarm alarm;
591
592     while ((alarm = Alarm.extractAlarm()) != null) {
593       // throttle alarm invocations by 5ms so quick alarms don't need
594
// extra threads
595
if (_concurrentAlarmThrottle < _runningAlarmCount) {
596         try {
597           Thread.sleep(5);
598         } catch (Throwable JavaDoc e) {
599         }
600       }
601
602       ThreadPool.getThreadPool().startPriority(alarm);
603     }
604
605     // thread.setName(oldName);
606
} finally {
607     _isRunning = false;
608       }
609     }
610   }
611 }
612
Popular Tags