User: osh     
  Date: 00/10/18 07:26:47

  Modified:    src/main/org/jboss/util/timeout TimeoutFactory.java
  Log:
  Each timeout callback now executes in a seperate thread.
  Added comments documenting internals.
  
  Revision  Changes    Path
  1.3       +179 -16   jboss/src/main/org/jboss/util/timeout/TimeoutFactory.java
  
  Index: TimeoutFactory.java
  ===================================================================
  RCS file: 
/products/cvs/ejboss/jboss/src/main/org/jboss/util/timeout/TimeoutFactory.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- TimeoutFactory.java       2000/10/03 04:07:33     1.2
  +++ TimeoutFactory.java       2000/10/18 14:26:47     1.3
  @@ -22,10 +22,37 @@
    *  allocating anything on the heap.
    *   
    *  @author <a href="[EMAIL PROTECTED]">Ole Husgaard</a>
  - *  @version $Revision: 1.2 $
  -*/
  + *  @version $Revision: 1.3 $
  + */
   public class TimeoutFactory {
   
  +  //  Multithreading notes:
  +  //
  +  //  While a TimeoutImpl is enqueued, its index field contains the index
  +  //  of the instance in the queue; that is, for 1 <= n <= size,
  +  //  q[n].index = n.
  +  //  Modifications of an enqueued TimeoutImpl instance may only happen
  +  //  in code synchronized on the TimeoutFactory instance that has it
  +  //  enqueued.
  +  //  Modifications on the priority queue may only happen while running in
  +  //  code synchronized on the TimeoutFactory instance that holds the queue.
  +  //  When a TimeoutImpl instance is no longer enqueued, its index field
  +  //  changes to one of the negative constants declared in the TimeoutImpl
  +  //  class.
  +  //  When a TimeoutImpl is not in use, its index field is TimeoutImpl.DONE
  +  //  and it is on the freeList.
  +  //
  +  //  Cancellation may race with the timeout.
  +  //  To avoid problems with this, the TimeoutImpl index field is set to
  +  //  TimeoutImpl.TIMEOUT when the TimeoutImpl is taken out of the queue.
  +  //  If a cancellation is attempted while the timeout callback is running,
  +  //  the TimeoutImpl field index is set to TimeoutImpl.CWAIT to indicate
  +  //  that someone is waiting for the timeout callback to finish.
  +  //  When the timeout callback returns it is checked if the index field
  +  //  was changed to TimeoutImpl.CWAIT, and if it was all waiting threads
  +  //  are notified. Finally the index field is set to TimeoutImpl.DONE, and
  +  //  the TimeoutImpl instance is discarded.
  +
     /**
      *  Our private Timeout implementation.
      */
  @@ -44,6 +71,43 @@
       }
     }
   
  +  /**
  +   *  A worker thread that fires the timeout.
  +   */
  +  private static class TimeoutWorker extends Thread {
  +    private TimeoutImpl work;
  + 
  +    /**
  +     *  Create a new instance.
  +     *
  +     *  @param work The timeout that should be fired.
  +     */
  +    TimeoutWorker(TimeoutImpl work) {
  +      this.work = work;
  +      setDaemon(false);
  +    }
  + 
  +    /**
  +     *  Override to fire the timeout.
  +     */
  +    public void run() {
  +      try {
  +        work.target.timedOut(work);
  +      } catch (Throwable t) {
  +        Logger.exception(t);
  +        //t.printStackTrace();
  +      }
  +      synchronized (work) {
  +        if (work.index == TimeoutImpl.CWAIT) {
  +          work.index = TimeoutImpl.DONE;
  +          work.notifyAll(); // wake up cancel() threads.
  +        } else
  +          work.index = TimeoutImpl.DONE;
  +      }
  +    }
  +  }
  +
  +
     /** Linked list of free TimeoutImpl instances. */
     private TimeoutImpl freeList;
   
  @@ -60,6 +124,115 @@
      *  and <code>j*2+1</code>. The children of a node always fire the timeout
      *  no earlier than the node.
      *
  +   *
  +   *  Or, more formally:
  +   *
  +   *  Only indices <code>1</code>..<code>size</code> of this array are used.
  +   *  All other indices contain the null reference.
  +   *  This array represent a balanced binary tree.
  +   *
  +   *  If <code>size</code> is <code>0</code> the tree is empty, otherwise
  +   *  the root of the tree is at index <code>1</code>.
  +   *
  +   *  Given an arbitrary node at index <code>n</code> that is not the root
  +   *  node, the parent node of <code>n</code> is at index <code>n/2</code>.
  +   *
  +   *  Given an arbitrary node at index <code>n</code>; if
  +   *  <code>2*n <= size</code> the node at <code>n</code> has its left child
  +   *  at index <code>2*n</code>, otherwise the node at <code>n</code> has
  +   *  no left child.
  +   *
  +   *  Given an arbitrary node at index <code>n</code>; if
  +   *  <code>2*n+1 <= size</code> the node at <code>n</code> has its right child
  +   *  at index <code>2*n+1</code>, otherwise the node at <code>n</code> has
  +   *  no right child.
  +   *
  +   *  The priority function is called T. Given a node <code>n</code>,
  +   *  <code>T(n)</code> denotes the absolute time (in milliseconds since
  +   *  the epoch) that the timeout for node <code>n</code> should happen.
  +   *  Smaller values of <code>T</code> means higher priority.
  +   *
  +   *  The tree satisfies the following invariant:
  +   *  <i>
  +   *  For any node <code>n</code> in the tree:
  +   *  If node <code>n</code> has a left child <code>l</code>,
  +   *  <code>T(n) <= T(l)</code>.
  +   *  If node <code>n</code> has a right child <code>r</code>,
  +   *  <code>T(n) <= T(r)</code>.
  +   *  </i>
  +   *
  +   *
  +   *  The invariant may be temporarily broken while executing synchronized
  +   *  on <code>this</code> instance, but is always reestablished before
  +   *  leaving the synchronized code. 
  +   *
  +   *  The node at index <code>1</code> is always the first node to timeout,
  +   *  as can be deduced from the invariant.
  +   *
  +   *  For the following algorithm pseudocode, the operation
  +   *  <code>swap(n,m)</code> denotes the exchange of the nodes at indices
  +   *  <code>n</code> and <code>m</code> in the tree.
  +   *
  +   *  Insertion of a new node happend as follows:
  +   *  <pre>
  +   *    IF size = q.length THEN
  +   *      "expand q array to be larger";
  +   *    ENDIF
  +   *    size <- size + 1;
  +   *    q[size] <- "new node";
  +   *    n <- size;
  +   *    WHILE n > 1 AND T(n/2) > T(n) DO
  +   *      swap(n/2, n);
  +   *      n <- n/2;
  +   *    ENDWHILE
  +   *  </pre>
  +   *  Proof that this insertion algorithm respects the invariant is left to
  +   *  the interested reader.
  +   *
  +   *  The removal algorithm is a bit more complicated. To remove the node
  +   *  at index <code>n</code>:
  +   *  <pre>
  +   *    swap(n, size);
  +   *    size <- size - 1;
  +   *    IF n > 1 AND T(n/2) > T(n) THEN
  +   *      WHILE n > 1 AND T(n/2) > T(n) DO
  +   *        swap(n/2, n);
  +   *        n <- n/2;
  +   *      ENDWHILE
  +   *    ELSE
  +   *      WHILE 2*n <= size DO
  +   *        IF 2*n+1 <= size THEN
  +   *          // Both children present
  +   *          IF T(2*n) <= T(2*n+1) THEN
  +   *            IF T(n) <= T(2*n) THEN
  +   *              EXIT;
  +   *            ENDIF
  +   *            swap(n, 2*n);
  +   *            n <- 2*n;
  +   *          ELSE
  +   *            IF T(n) <= T(2*n+1) THEN
  +   *              EXIT;
  +   *            ENDIF
  +   *            swap(n, 2*n+1);
  +   *            n <- 2*n+1;
  +   *          ENDIF
  +   *        ELSE
  +   *          // Only left child, right child not present.
  +   *          IF T(n) <= T(2*n) THEN
  +   *            EXIT;
  +   *          ENDIF
  +   *          swap(n, 2*n);
  +   *          n <- 2*n;
  +   *        ENDIF
  +   *      ENDWHILE
  +   *    ENDIF
  +   *  </pre>
  +   *  Proof that this removal algorithm respects the invariant is left to
  +   *  the interested reader. Really, I am not going to prove it here.
  +   *
  +   *  If you are interested, you can find this data structure and its
  +   *  associated operations in most textbooks on algorithmics.
  +   *
      *  @see checkTree
      */
     private TimeoutImpl[] q;
  @@ -307,7 +480,6 @@
       }
     }
   
  -
     /**
      *  Timeout worker method.
      *  This method never returns. Whenever it is time to do a timeout,
  @@ -339,22 +511,13 @@
   
         // Do work, if any
         if (work != null) {
  -        try {
  -          work.target.timedOut(work);
  -        } catch (Throwable t) {
  -          Logger.exception(t);
  -          //t.printStackTrace();
  -        }
  -        synchronized (work) {
  -          if (work.index == TimeoutImpl.CWAIT) {
  -            work.index = TimeoutImpl.DONE;
  -            work.notifyAll(); // wake up cancel() threads.
  -          } else
  -            work.index = TimeoutImpl.DONE;
  -        }
  +        // Create a new thread to do the callback.
  +        TimeoutWorker worker = new TimeoutWorker(work);
  +        worker.start();
         }
       }
     }
  +
   
     /** Our singleton instance. */
     static private TimeoutFactory singleton;
  
  
  

Reply via email to