User: fleury  
  Date: 00/09/07 19:25:48

  Added:       src/main/org/jboss/tm/timeout TimeoutTarget.java
                        TimeoutFactory.java Timeout.java
  Log:
  Ole husgaard updates
  
  Revision  Changes    Path
  1.1                  jboss/src/main/org/jboss/tm/timeout/TimeoutTarget.java
  
  Index: TimeoutTarget.java
  ===================================================================
  /*
  * jBoss, the OpenSource EJB server
  *
  * Distributable under GPL license.
  * See terms of license at gnu.org.
  */
  package org.jboss.util.timeout;
  
  
  /**
   *  The interface of objects that can receive timeouts.
   *   
   *  @author <a href="[EMAIL PROTECTED]">Ole Husgaard</a>
   *  @version $Revision: 1.1 $
  */
  public interface TimeoutTarget {
     /**
      *  The timeout callback function is invoked when the timeout expires.
      */
     public void timedOut(Timeout timeout);
  }
  
  
  
  
  1.1                  jboss/src/main/org/jboss/tm/timeout/TimeoutFactory.java
  
  Index: TimeoutFactory.java
  ===================================================================
  /*
  * jBoss, the OpenSource EJB server
  *
  * Distributable under GPL license.
  * See terms of license at gnu.org.
  */
  package org.jboss.util.timeout;
  
  
  /**
   *  The timeout factory.
   *
   *  This is written with performance in mind. In case of <code>n</code>
   *  active timeouts, creating, cancelling and firing timeouts all operate
   *  in time <code>O(log(n))</code>.
   *
   *  If a timeout is cancelled, the timeout is not discarded. Instead the
   *  timeout is saved to be reused for another timeout. This means that if
   *  no timeouts are fired, this class will eventually operate without
   *  allocating anything on the heap.
   *   
   *  @author <a href="[EMAIL PROTECTED]">Ole Husgaard</a>
   *  @version $Revision: 1.1 $
  */
  public class TimeoutFactory {
  
    /**
     *  Our private Timeout implementation.
     */
    private class TimeoutImpl implements Timeout {
      static final int DONE = -1; // done, may be finalized
      static final int TIMEOUT = -2; // target being called
      static final int CWAIT = -3; // target being called and cancel waiting
  
      int index; // index in queue, or one of constants above.
      long time;
      TimeoutTarget target;
  
      public void cancel() {
        TimeoutFactory.this.cancelTimeout(this);
      }
    }
  
    /** The size of the timeout queue. */
    private int size;
  
    /**
     *  Our priority queue.
     *  This is a binary tree. If nonempty, the root is at index 1, and all
     *  nodes are at indices 1..size. Nodes with index greater than size
     *  are considered null. Index 0 is never used.
     *  Children of the node at index <code>j</code> are at <code>j*2</code>
     *  and <code>j*2+1</code>. The children of a node always fire the timeout
     *  no later than the node.
     */
    private TimeoutImpl[] q;
  
    /**
     *  Debugging helper.
     */
    private void assert(boolean expr) {
      if (!expr) {
        System.err.println("***** assert failed *****");
        Thread.currentThread().dumpStack();
      }
    }
  
    /**
     *  Swap two nodes in the tree.
     */
    private void swap(int a, int b) {
        assert(q[a].index == a);
        assert(q[b].index == b);
        TimeoutImpl temp = q[a];
        q[a] = q[b];
        q[a].index = a;
        q[b] = temp;
        q[b].index = b;
    }
  
    /**
     *  A new node has been added at index <code>index</code>.
     *  Normalize the tree by moving the new node up the tree.
     */
    private void normalizeUp(int index) {
      assert(index > 0);
      assert(index <= size);
      assert(q[index] != null);
      long t = q[index].time;
  
      if (index == 1)
        return; // at root
  
      int p = index >> 1;
  
      while (q[p].time > t) {
        swap(p, index);
  
        if (p == 1)
          return; // at root
  
        index = p;
        p >>= 1;
      }
    }
  
    /**
     *  Remove a node from the tree and normalize.
     *  Returns the removed node.
     */
    private TimeoutImpl removeNode(int index) {
      assert(index > 0);
      assert(index <= size);
      TimeoutImpl res = q[index];
  
      // one less entry
      if (--size <= 1)
        return res; // Already normal
  
      // Normalize
      int c = index << 1;
      long t = res.time;
  
      while (q[c].time <= t) {
        swap(index, c);
  
        index = c;
  
        c <<= 1;
        if (c > size)
          break; // node at index is a leaf
      }
      return res;
    }
  
  
    /**
     *  Create a new timeout.
     */
    private synchronized Timeout newTimeout(long time, TimeoutTarget target) {
      if (size == q.length) {
        TimeoutImpl[] newQ = new TimeoutImpl[2*q.length];
        System.arraycopy(q, 0, newQ, 0, q.length);
        q = newQ;
      }
      ++size;
      if (q[size] == null)
        q[size] = new TimeoutImpl();
  
      TimeoutImpl timeout = q[size];
  
      timeout.index = size;
      timeout.time = time;
      timeout.target = target;
  
      normalizeUp(size);
  
      if (timeout.index == 1 || size == 1)
        notify();
  
      return timeout;
    }
  
    /**
     *  Cancel a timeout.
     */
    private void dropTimeout(TimeoutImpl timeout) {
      synchronized (this) {
        if (timeout.index > 0) {
          removeNode(timeout.index);
          return;
        }
      }
  
      // Timeout has already started, wait until done.
      synchronized (timeout) {
        if (timeout.index == TimeoutImpl.TIMEOUT) {
          // Wait to avoid race with the actual timeout that is happening now.
          timeout.index = TimeoutImpl.CWAIT;
          while (timeout.index == TimeoutImpl.CWAIT) {
            try {
              timeout.wait();
            } catch (InterruptedException ex) {}
          }
        }
      }
    }
  
  
    /**
     *  Cancel a timeout.
     */
    private void cancelTimeout(Timeout timeout) {
      if (timeout == null)
        throw new IllegalArgumentException("Null timeout");
      if (!(timeout instanceof TimeoutImpl))
        throw new IllegalArgumentException("Unknown timeout");
  
      dropTimeout((TimeoutImpl)timeout);
    }
  
    /**
     *  Timeout worker method.
     *  This method never returns. Whenever it is time to do a timeout,
     *  the callback method is called from here.
     */
    private void doWork() {
      while (true) {
        TimeoutImpl work = null;
  
        synchronized (this) {
          if (size == 0) {
            try {
              wait();
            } catch (InterruptedException ex) {}
          } else {
            long now = System.currentTimeMillis();
            if (q[1].time > now) {
              try {
                wait(q[1].time - now);
              } catch (InterruptedException ex) {}
            } else {
              work = removeNode(1);
              q[work.index] = null;
              work.index = TimeoutImpl.TIMEOUT;
            }
          }
        }
  
        if (work != null) {
          work.target.timedOut(work);
          synchronized (work) {
            if (work.index == TimeoutImpl.CWAIT) {
              work.index = TimeoutImpl.DONE;
              work.notify(); // wake up cancel() thread.
            }
          }
        }
      }
    }
  
    /** Our singleton instance. */
    static private TimeoutFactory singleton;
  
    /** Our private constructor. */
    private TimeoutFactory() {
      size = 0;
      q = new TimeoutImpl[16];
    }
  
    /**
     *  Initialize class.
     *  The will initialize the singleton and create a single
     *  worker thread.
     */
    static {
      singleton = new TimeoutFactory();
      Thread thread = new Thread() {
        public void run() {
          singleton.doWork();
        }
      };
      thread.setDaemon(true);
      thread.start();
    }
  
    /**
     *  Schedule a new timeout.
     */
    static public Timeout createTimeout(long time, TimeoutTarget target) {
      if (time <= 0)
        throw new IllegalArgumentException("Time not positive");
      if (target == null)
        throw new IllegalArgumentException("Null target");
  
      return singleton.newTimeout(time, target);
    }
  
  }
   
  
  
  
  1.1                  jboss/src/main/org/jboss/tm/timeout/Timeout.java
  
  Index: Timeout.java
  ===================================================================
  /*
  * jBoss, the OpenSource EJB server
  *
  * Distributable under GPL license.
  * See terms of license at gnu.org.
  */
  package org.jboss.util.timeout;
  
  
  /**
   *  The public interface of timeouts.
   *   
   *  @author <a href="[EMAIL PROTECTED]">Ole Husgaard</a>
   *  @version $Revision: 1.1 $
  */
  public interface Timeout {
     /**
      *  Cancel this timeout.
      *
      *  It is guaranteed that on return from this method this timer is
      *  no longer active. This means that either it has been cancelled and
      *  the timeout will not happen, or (in case of late cancel) the
      *  timeout has happened and the timeout callback function has returned.
      *
      *  On return from this method this instance should no longer be
      *  used. The reason for this is that an implementation may reuse
      *  cancelled timeouts, and at return the instance may already be
      *  in use for another timeout.
      */
     public void cancel();
  }
  
  
  
  

Reply via email to