summaryrefslogtreecommitdiff
path: root/java/src
diff options
context:
space:
mode:
authorpjain <pjain@ae88bc3d-4319-0410-8dbf-d08b4c9d3795>1996-11-23 23:56:20 +0000
committerpjain <pjain@ae88bc3d-4319-0410-8dbf-d08b4c9d3795>1996-11-23 23:56:20 +0000
commit82dae81a63dcc3a382f524898e86c69cd82f2aaf (patch)
treee24689caca5c69ec530b4160011bee5ebb3dc48a /java/src
parentf6dfda46b9087b405a0fb67ba9a2d39d1f2e1e7b (diff)
downloadATCD-82dae81a63dcc3a382f524898e86c69cd82f2aaf.tar.gz
Class that provides an interface to timers.
Diffstat (limited to 'java/src')
-rw-r--r--java/src/TimerQueue.java379
1 files changed, 379 insertions, 0 deletions
diff --git a/java/src/TimerQueue.java b/java/src/TimerQueue.java
new file mode 100644
index 00000000000..0aed48a84ae
--- /dev/null
+++ b/java/src/TimerQueue.java
@@ -0,0 +1,379 @@
+/*************************************************
+ *
+ * = PACKAGE
+ * ACE.Reactor
+ *
+ * = FILENAME
+ * TimerQueue.java
+ *
+ *@author Prashant Jain
+ *
+ *************************************************/
+package ACE.Reactor;
+
+import java.util.*;
+import ACE.ASX.*;
+
+class TimerNode
+{
+ public TimerNode (EventHandler handler,
+ Object arg,
+ TimeValue timerValue,
+ TimeValue interval,
+ TimerNode next,
+ int timerId)
+ {
+ this.handler_ = handler;
+ this.arg_ = arg;
+ this.timerValue_ = timerValue;
+ this.interval_ = interval;
+ this.next_ = next;
+ this.timerId_ = timerId;
+ }
+
+ public EventHandler handler_;
+ // Handler to invoke <handleTimeout> on when a timeout occurs.
+
+ public Object arg_;
+ // Argument to pass to <handleTimeout>.
+
+ public TimeValue timerValue_;
+ // Time until the timer expires.
+
+ public TimeValue interval_;
+ // If this is a periodic timer this holds the time until the next
+ // timeout.
+
+ public TimerNode next_;
+ // Pointer to next timer.
+
+ public int timerId_;
+ // Id of this timer (used to cancel timers before they expire).
+}
+
+class WaitObject extends TimedWait
+{
+ public boolean condition ()
+ {
+ return this.condition_;
+ }
+
+ public void condition (boolean c)
+ {
+ this.condition_ = c;
+ }
+
+ private boolean condition_ = false;
+}
+
+/**
+ * <hr>
+ * <h2>SYNOPSIS</h2>
+ *<blockquote>
+ * Class that provides an interface to timers.
+ *
+ *</blockquote>
+ *
+ * <h2>DESCRIPTION</h2>
+ *<blockquote>
+ * This is a simple implementation that keeps a linked list of
+ * absolute timers. It allows multiple timers to be scheduled
+ * and returns a timer id for each timer scheduled. In addition,
+ * it allows periodic timers to be scheduled.
+ *</blockquote>
+ */
+public class TimerQueue implements Runnable
+{
+ /**
+ * Default Constructor
+ */
+ public TimerQueue ()
+ {
+ new Thread (this).start ();
+ }
+
+ /**
+ * This is the work horse of TimerQueue. It forms the event loop and
+ * takes care of all scheduling.
+ */
+ public void run ()
+ {
+ TimeValue timeout = null;
+ TimeValue earliest = null;
+
+ for (;;)
+ {
+ synchronized (this.obj_)
+ {
+ earliest = this.earliestTime ();
+ if (earliest != null)
+ timeout = TimeValue.minus (earliest, TimeValue.getTimeOfDay ());
+ else
+ timeout = new TimeValue ();
+ try
+ {
+ // System.out.println (timeout);
+ // Extract the earliest time from the queue and do a timed wait
+ this.obj_.timedWait (timeout);
+
+ // We have been notified. Check to see if we need to
+ // restart the wait with a different timeout
+ if (this.reset_)
+ {
+ this.reset_ = false;
+ this.obj_.condition (false);
+ timeout = TimeValue.minus (this.earliestTime (), TimeValue.getTimeOfDay ());
+ }
+ }
+ catch (TimeoutException e)
+ {
+ // Timeout occurred. Call handleTimeout on appropriate
+ // Event Handlers
+ this.dispatchHandlers ();
+ }
+ catch (InterruptedException e)
+ {
+ }
+ }
+ }
+ }
+
+ /**
+ * Check if the queue is empty.
+ *@return true if queue is empty, else false.
+ */
+ boolean isEmpty ()
+ {
+ return this.head_ == null;
+ }
+
+ /**
+ * Get the node of the earliest node in the TimerQueue.
+ *@return the time of the earlier node in the TimerQueue.
+ */
+ TimeValue earliestTime ()
+ {
+ synchronized (this.obj_)
+ {
+ if (!this.isEmpty ())
+ return this.head_.timerValue_;
+ else
+ return null;
+ }
+ }
+
+ /**
+ * Schedule an <EventHandler> that will expire after <delta> amount
+ * of time. If it expires then <obj> is passed in as the value to
+ * the <EventHandler>'s <handleTimeout> callback method. This method
+ * returns a timer id that uniquely identifies the timer and can be
+ * used to cancel the timer before it expires.
+ *@param handler Event Handler that is to be scheduled with the timer
+ *@param obj Object that is passed back to the Event Handler when
+ * timeout occurs (Asynchronous Completion Token)
+ *@param delta amount of time for which to schedule the timer
+ *@return id of the timer scheduled
+ */
+ public int scheduleTimer (EventHandler handler,
+ Object obj,
+ TimeValue delta)
+ {
+ return this.scheduleTimer (handler, obj, delta, TimeValue.zero);
+ }
+
+ /**
+ * Schedule an <EventHandler> that will expire after <delta> amount
+ * of time. If it expires then <obj> is passed in as the value to
+ * the <EventHandler>'s <handleTimeout> callback method. If
+ * <interval> is != to <TimeValue.zero> then it is used to
+ * reschedule the <EventHandler> automatically. This method
+ * returns a timer id that uniquely identifies the timer and can be
+ * used to cancel the timer before it expires.
+ *@param handler Event Handler that is to be scheduled with the timer
+ *@param arg Object that is passed back to the Event Handler when
+ * timeout occurs (Asynchronous Completion Token)
+ *@param timeout amount of time for which to schedule the timer
+ *@param interval amount of time to use to reschedule the timer
+ *@return id of the timer scheduled
+ */
+ public int scheduleTimer (EventHandler handler,
+ Object arg,
+ TimeValue timeout,
+ TimeValue interval)
+ {
+ // Increment the sequence number (it will wrap around).
+ this.timerId_++;
+ TimeValue futureTime = TimeValue.plus (timeout, TimeValue.getTimeOfDay ());
+ TimerNode node = new TimerNode (handler,
+ arg,
+ futureTime,
+ interval,
+ null,
+ this.timerId_);
+ synchronized (this.obj_)
+ {
+ if (this.isEmpty () || futureTime.lessThan (this.earliestTime ()))
+ {
+ // Place at the beginning of the list.
+ this.reschedule (node);
+ this.obj_.condition (true);
+ this.reset_ = true;
+
+ // Notify the waiting thread so that it can reschedule
+ // using the earliest timeout
+ this.obj_.notify ();
+ }
+ else // Place in the middle of the list somewhere.
+ {
+ this.reschedule (node);
+ }
+ }
+ return this.timerId_;
+ }
+
+
+ /**
+ * Cancel the single timer associated with <timerId>.
+ *@param timerId id of the timer that needs to be cancelled.
+ *@return Object that was passed in when timer was scheduled
+ * (Asynchronous Completion Token).
+ */
+ public Object cancelTimer (int timerId)
+ {
+ TimerNode prev = null;
+ TimerNode curr = null;
+
+ // Try to locate the TimerNode that matches the timerId.
+
+ for (curr = this.head_;
+ curr != null && curr.timerId_ != timerId;
+ curr = curr.next_)
+ prev = curr;
+
+ if (curr != null)
+ {
+ if (prev == null)
+ this.head_ = curr.next_;
+ else
+ prev.next_ = curr.next_;
+
+ return curr.arg_;
+ }
+ return null;
+ }
+
+ /**
+ * Cancel all timers associated with <Event Handler>.
+ *@param handler Event Handler whose associated timers need to be cancelled.
+ */
+ public void cancelTimer (EventHandler handler)
+ {
+ TimerNode prev = null;
+ TimerNode curr = this.head_;
+
+ while (curr != null)
+ {
+ if (curr.handler_ == handler)
+ {
+ if (prev == null)
+ {
+ this.head_ = curr.next_;
+ curr = this.head_;
+ }
+ else
+ {
+ prev.next_ = curr.next_;
+ curr = prev.next_;
+ }
+ }
+ else
+ {
+ prev = curr;
+ curr = curr.next_;
+ }
+ }
+ }
+
+ // Call handleTimeout() on all handlers whose timers have expired.
+ private void dispatchHandlers ()
+ {
+ TimeValue currentTime = TimeValue.getTimeOfDay ();
+
+ for (;;)
+ {
+ if (this.isEmpty () || this.earliestTime ().greaterThan (currentTime))
+ break; // There aren't any more timers eligible to expire.
+
+ TimerNode expired = this.head_;
+ EventHandler handler = expired.handler_;
+ Object arg = expired.arg_;
+ int result;
+
+ this.head_ = this.head_.next_;
+
+ // Check whether this is an interval timer.
+ if (expired.interval_.greaterThan (TimeValue.zero))
+ {
+ // Make sure that we skip past values that have already
+ // "expired".
+ do
+ expired.timerValue_.plusEquals (expired.interval_);
+ while (expired.timerValue_.lessThanEqual (currentTime));
+
+ // Since this is an interval timer, we need to reschedule
+ // it.
+ this.reschedule (expired);
+ }
+
+ // Perform the callback.
+ result = handler.handleTimeout (currentTime, arg);
+
+ if (result == -1)
+ this.cancelTimer (handler);
+ }
+ }
+
+ // Reschedule a TimerNode by inserting it at the appropriate
+ // position in the queue.
+ private void reschedule (TimerNode expired)
+ {
+ if (this.isEmpty () ||
+ expired.timerValue_.lessThan (this.earliestTime ()))
+ {
+ expired.next_ = this.head_;
+ this.head_ = expired;
+ }
+ else
+ {
+ TimerNode prev = this.head_;
+ TimerNode after = this.head_.next_;
+
+ // Locate the proper position in the queue.
+
+ while (after != null
+ && expired.timerValue_.greaterThan (after.timerValue_))
+ {
+ prev = after;
+ after = after.next_;
+ }
+
+ expired.next_ = after;
+ prev.next_ = expired;
+ }
+ }
+
+ private WaitObject obj_ = new WaitObject ();
+ // Synchronization object (as well as object to use to do wait on)
+
+ private TimerNode head_;
+ // Pointer to linked list of TimerHandles.
+
+ private int timerId_;
+ // Keeps track of the timer id that uniquely identifies each timer.
+ // This id can be used to cancel a timer via the <cancel (int)>
+ // method.
+
+ private boolean reset_;
+ // Flag indicating whether to start the wait again
+}
+