home *** CD-ROM | disk | FTP | other *** search
/ Chip 1998 November / Chip_1998-11_cd.bin / tema / Cafe / jfc.bin / TimerQueue.java < prev    next >
Text File  |  1998-02-26  |  11KB  |  373 lines

  1. /*
  2.  * @(#)TimerQueue.java    1.18 98/02/02
  3.  *
  4.  * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
  5.  *
  6.  * This software is the confidential and proprietary information of Sun
  7.  * Microsystems, Inc. ("Confidential Information").  You shall not
  8.  * disclose such Confidential Information and shall use it only in
  9.  * accordance with the terms of the license agreement you entered into
  10.  * with Sun.
  11.  *
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
  13.  * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  14.  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  15.  * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
  16.  * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
  17.  * THIS SOFTWARE OR ITS DERIVATIVES.
  18.  *
  19.  */
  20.  
  21.  
  22. /**
  23.  * Internal class to manage all Timers using one thread.
  24.  *
  25.  *
  26.  * @version 1.18 02/02/98
  27.  * @author Dave Moore
  28.  */
  29.  
  30. package com.sun.java.swing;
  31.  
  32. import java.util.*;
  33. import java.awt.Toolkit;
  34. import java.awt.Panel;
  35. import java.awt.Graphics;
  36. import java.awt.Window;
  37. import java.applet.Applet;
  38. import java.awt.Color;
  39.  
  40. /** Private class to manage a queue of Timers. The Timers are chained
  41.   * together in a linked list sorted by the order in which they will expire.
  42.   */
  43. class TimerQueue implements Runnable {
  44.     Timer firstTimer;
  45.     boolean running;
  46.  
  47.     private static final Object sharedInstanceKey = 
  48.         new StringBuffer("TimerQueue.sharedInstanceKey");
  49.     private static final Object expiredTimersKey = 
  50.         new StringBuffer("TimerQueue.expiredTimersKey");
  51.  
  52.     // This static variable can be shared across AppContexts.
  53.     private static Hashtable timerPanels = null;
  54.  
  55.     public TimerQueue() {
  56.         super();
  57.  
  58.         // Force Toolkit to be initialized before any timers go off,
  59.         // so the Toolkit threads won't inherit the timer threads'
  60.         // daemon state.  This is a workaround for a bug in pre-1.1.4
  61.         // Sun VMs (#4066493).
  62.         Toolkit t = Toolkit.getDefaultToolkit();
  63.  
  64.         // Now start the TimerQueue thread.
  65.         start();
  66.     }
  67.  
  68.     public static TimerQueue sharedInstance() {
  69.         synchronized (TimerQueue.class) {
  70.             TimerQueue sharedInst = (TimerQueue)
  71.                 SwingUtilities.appContextGet(sharedInstanceKey);
  72.             if (sharedInst == null) {
  73.                 sharedInst = new TimerQueue();
  74.                 SwingUtilities.appContextPut(sharedInstanceKey, 
  75.                                              sharedInst);
  76.             }
  77.             return sharedInst;
  78.         }
  79.     }
  80.  
  81.     synchronized void start() {
  82.         if (running) {
  83.             throw new RuntimeException("Can't start a TimerQueue that is already running");
  84.         } else {
  85.             Thread timerThread = new Thread(this, "TimerQueue");
  86.  
  87.             try {
  88.                 if (timerThread.getPriority() > Thread.MIN_PRIORITY)
  89.                    timerThread.setPriority(timerThread.getPriority() - 1);
  90.  
  91.                 timerThread.setDaemon(true);
  92.             } catch (SecurityException e) {
  93.             }
  94.             timerThread.start();
  95.             running = true;
  96.         }
  97.     }
  98.  
  99.     synchronized void stop() {
  100.         running = false;
  101.         notify();
  102.     }
  103.  
  104.     synchronized void addTimer(Timer timer, long expirationTime) {
  105.         Timer previousTimer, nextTimer;
  106.  
  107.         // If the Timer is already in the queue, then ignore the add.
  108.         if (timer.running) {
  109.             return;
  110.     }
  111.  
  112.         previousTimer = null;
  113.         nextTimer = firstTimer;
  114.  
  115.         // Insert the Timer into the linked list in the order they will
  116.         // expire.  If two timers expire at the same time, put the newer entry
  117.         // later so they expire in the order they came in.
  118.  
  119.         while (nextTimer != null) {
  120.             if (nextTimer.expirationTime > expirationTime)
  121.                 break;
  122.  
  123.             previousTimer = nextTimer;
  124.             nextTimer = nextTimer.nextTimer;
  125.         }
  126.  
  127.         if (previousTimer == null)
  128.             firstTimer = timer;
  129.         else
  130.             previousTimer.nextTimer = timer;
  131.  
  132.         timer.expirationTime = expirationTime;
  133.         timer.nextTimer = nextTimer;
  134.         timer.running = true;
  135.         notify();
  136.     }
  137.  
  138.     synchronized void removeTimer(Timer timer) {
  139.         boolean found;
  140.         Timer previousTimer, nextTimer;
  141.  
  142.         if (!timer.running)
  143.             return;
  144.  
  145.         previousTimer = null;
  146.         nextTimer = firstTimer;
  147.         found = false;
  148.  
  149.         while (nextTimer != null) {
  150.             if (nextTimer == timer) {
  151.                 found = true;
  152.                 break;
  153.             }
  154.  
  155.             previousTimer = nextTimer;
  156.             nextTimer = nextTimer.nextTimer;
  157.         }
  158.  
  159.         if (!found)
  160.             return;
  161.  
  162.         if (previousTimer == null)
  163.             firstTimer = timer.nextTimer;
  164.         else
  165.             previousTimer.nextTimer = timer.nextTimer;
  166.  
  167.         timer.expirationTime = 0;
  168.         timer.nextTimer = null;
  169.         timer.running = false;
  170.     }
  171.  
  172.     synchronized boolean containsTimer(Timer timer) {
  173.         return timer.running;
  174.     }
  175.  
  176.     // If there are a ton of timers, this method may never return.  It loops
  177.     // checking to see if the head of the Timer list has expired.  If it has,
  178.     // it posts the Timer and reschedules it if necessary.
  179.  
  180.     synchronized long postExpiredTimers() {
  181.         long currentTime, timeToWait;
  182.         Timer timer;
  183.  
  184.         // The timeToWait we return should never be negative and only be zero
  185.         // when we have no Timers to wait for.
  186.  
  187.         do {
  188.             timer = firstTimer;
  189.             if (timer == null)
  190.                 return 0;
  191.  
  192.             currentTime = System.currentTimeMillis();
  193.             timeToWait = timer.expirationTime - currentTime;
  194.  
  195.             if (timeToWait <= 0) {
  196.                 if (timerPanels != null) {
  197.                     /* If an applet is running we assume that access to the event
  198.                      * queue isn't allowed (see initAppletTimer). So instead of
  199.                      * posting a TimerEvent directly we add the timer to the 
  200.                      * expiredTimers vector and queue an expose event by calling
  201.                      * timerPanel.repaint().  See the TimerPanel class.
  202.                      */
  203.                     TimerPanel timerPanel = getActiveTimerPanel();
  204.                     if (timerPanel != null) {
  205.                         Vector expiredTimers = getExpiredTimers();
  206.                         synchronized(expiredTimers) {
  207.                             expiredTimers.addElement(timer);
  208.                         }
  209.                         timerPanel.repaint();
  210.                     } else {
  211.                         /* There are applets, but none are currently visible:
  212.                          * drop event.
  213.                          */
  214.                     }
  215.                 } else {
  216.                     try {
  217.                         timer.post();  // have timer post an event
  218.                     } catch (SecurityException e) {}
  219.                 }
  220.                 removeTimer(timer);
  221.  
  222.                 // This tries to keep the interval uniform at the cost of
  223.                 // drift.
  224.  
  225.                 if (timer.isRepeats())
  226.                     addTimer(timer, currentTime + timer.getDelay());
  227.  
  228.                 // Allow other threads to call addTimer() and removeTimer()
  229.                 // even when we are posting Timers like mad.  Since the wait()
  230.                 // releases the lock, be sure not to maintain any state
  231.                 // between iterations of the loop.
  232.  
  233.                 try {
  234.                     wait(1);
  235.                 } catch (InterruptedException e) {
  236.                 }
  237.             }
  238.         } while (timeToWait <= 0);
  239.  
  240.         return timeToWait;
  241.     }
  242.  
  243.     public synchronized void run() {
  244.         long timeToWait;
  245.  
  246.         while (running) {
  247.             timeToWait = postExpiredTimers();
  248.             try {
  249.                 wait(timeToWait);
  250.             } catch (InterruptedException e) {
  251.             }
  252.         }
  253.     }
  254.  
  255.     public synchronized String toString() {
  256.         StringBuffer buf;
  257.         Timer nextTimer;
  258.  
  259.         buf = new StringBuffer();
  260.         buf.append("TimerQueue (");
  261.  
  262.         nextTimer = firstTimer;
  263.         while (nextTimer != null) {
  264.             buf.append(nextTimer.toString());
  265.  
  266.             nextTimer = nextTimer.nextTimer;
  267.             if (nextTimer != null)
  268.                 buf.append(", ");
  269.         }
  270.  
  271.         buf.append(")");
  272.         return buf.toString();
  273.     }
  274.  
  275.     /** 
  276.      * Applets can't post events to the system event queue (JDK1.1.x)
  277.      * however a timers ActionListener.actionPerformed() method must be 
  278.      * run on the event dispatching thread.  To enable this we create
  279.      * an off screen java.awt.Panel whose update() method dequeues 
  280.      * timers that are ready to run, see the TimerPanel class below.
  281.      * The TimerQueue.postTimers() method applies repaint to the panel 
  282.      * which causes a "paint" event to be queued for the event dispatching
  283.      * thread.  The event dispatching thread handles paint events
  284.      * by calling Component.update().
  285.      */
  286.     static synchronized void initAppletTimer(JApplet applet) {
  287.         Hashtable timerPanels = getTimerPanels();
  288.         TimerPanel timerPanel = (TimerPanel)timerPanels.get(applet);
  289.         if(timerPanel == null) {
  290.             timerPanel = new TimerPanel();
  291.         applet.getLayeredPane().add(timerPanel);
  292.             timerPanels.put(applet,timerPanel);
  293.         }
  294.     }
  295.  
  296.     static synchronized void removeAppletTimer(JApplet applet) {
  297.         getTimerPanels().remove(applet);
  298.     }
  299.  
  300.     /** 
  301.      * Return the first usable timer panel 
  302.      */
  303.     static synchronized TimerPanel getActiveTimerPanel() {
  304.         Enumeration e = getTimerPanels().elements();
  305.         TimerPanel tp;
  306.         while(e.hasMoreElements()) {
  307.             tp = (TimerPanel) e.nextElement();
  308.             if(tp.isShowing())
  309.                 return tp;
  310.         }
  311.         return null;
  312.     }
  313.  
  314.     static Hashtable getTimerPanels() {
  315.         if (timerPanels == null) {
  316.             timerPanels = new Hashtable(2);
  317.         }
  318.         return timerPanels;
  319.     }
  320.  
  321.     static Vector getExpiredTimers() {
  322.         Vector v = (Vector)SwingUtilities.appContextGet(expiredTimersKey);
  323.         if (v == null) {
  324.             v = new Vector();
  325.             SwingUtilities.appContextPut(expiredTimersKey, v);
  326.         }
  327.         return v;
  328.     }
  329.  
  330.     /**
  331.      * @see #initAppletTimer
  332.      */
  333.     private static class TimerPanel extends Panel 
  334.     {
  335.     TimerPanel () {
  336.         super();
  337.         setBounds(0, 0, 1, 1);
  338.             //setBackground(Color.red);
  339.     }
  340.  
  341.         public void update(Graphics g) 
  342.     {
  343.             Vector expiredTimers = 
  344.                 TimerQueue.sharedInstance().getExpiredTimers();
  345.         Vector shouldFire;
  346.  
  347.         /* Copy the expiredTimers vector so we don't have to 
  348.          * synchronize access to it while firing the timers.
  349.          */
  350.             synchronized (expiredTimers) {
  351.         if (expiredTimers.size() == 0) {
  352.             return;
  353.         }
  354.         Enumeration e = expiredTimers.elements();
  355.         shouldFire = new Vector();
  356.                 while(e.hasMoreElements()) {
  357.             shouldFire.addElement(e.nextElement());
  358.                 }
  359.         expiredTimers = new Vector();
  360.                 SwingUtilities.appContextPut(expiredTimersKey, expiredTimers);
  361.             }
  362.  
  363.         /* Fire the timers
  364.          */
  365.         int nTimers = shouldFire.size();
  366.         for(int i = 0; i < nTimers; i++) {
  367.         ((Timer)shouldFire.elementAt(i)).fire();
  368.         }
  369.         }
  370.     }
  371.  
  372. }
  373.