Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-4260473

javax.swing.TimerQueue: timer fires late when added too quickly

XMLWordPrintable

    • beta
    • generic
    • generic



      Name: wl91122 Date: 08/06/99


      I've discovered a bug in javax.swing.TimerQueue. The effect is that
      queueing a long delay timer, immediately followed by a short delay
      timer results in the shorter delay timer not firing until the long
      delay timeout is reached. The timers still fire in the proper order,
      it's just that the short delay timer fires late.

      With a short delay of 1.5 sec and a long delay of 120 sec, the effect
      is quite dramatic.

      At the bottom, I've included a test case. It prints "fired 100", then
      waits 2 minutes and prints "fired 1500" and "fired 120000". The 1500
      print should occur just 1.5 seconds after the 100 print.

      I'm using swing-1.1beta3 on linux with jdk1.1.7.

      Any idea when 1.2 for linux will be available?

      Here's the relevant code. I've elided complicated parts that aren't
      directly involved in the bug with comments indicating what goes on
      there.

      class TimerQueue implements Runnable {

          synchronized void addTimer(Timer timer, long expirationTime) {
              // adds the timer to the queue, then calls:
              notify();
          }

          synchronized long postExpiredTimers() {
              long timeToWait;
              Timer timer;

              do {
                  // calculates timeToWait based on the first timer in the queue
                  // firing the timer if timeToWait <= 0

                  try {
                      wait(1);
                  } catch (InterruptedException e) {
                  }
              } while (timeToWait <= 0);

              return timeToWait;
          }
          
          public synchronized void run() {
              long timeToWait;

              while (running) {
                  timeToWait = postExpiredTimers();
                  try {
                      wait(timeToWait);
                  }
                  catch (InterruptedException e) { }
              }
          }
      }

      Sequence of events is as follows:

      1) addTimer is called with a long expirationTime. This becomes the
      first element of the queue. addTimer calls notify().

      2) The notify() wakes up the TimerQueue thread, which is at the wait()
      in run(). run() loops back and calls postExpiredTimers().

      3) postExpiredTimers() calculates the timeToWait based on the long
      expirationTime in the first timer, and calls wait(1).

      4) Back in the user thread, addTimer is called with a short
      expirationTime. This timer is inserted into the queue in front of the
      long timer. addTimer calls notify().

      5) The notify() wakes up the TimerQueue thread, which is at the
      wait(1) in postExpiredTimers(). timeToWait is the one calculated for
      the long timer. timeToWait > 0, so it is returned.

      6) run() calls wait() with the long delay.

      7) the user thread continues, but the short delay timer doesn't fire
      until the long delay timeout is reached.

      Threads are evil.

      -eric messick


      import java.awt.event.ActionListener;
      import java.awt.event.ActionEvent;
      import javax.swing.Timer;

      public class SwingTimerBug implements ActionListener, Runnable
      {
          private int m_delay;
          
          static public void
          main(String args[]) {
              SwingTimerBug initialNotify = new SwingTimerBug(100);
                  Timer initialTimer = new Timer(100, initialNotify);
                  initialTimer.setRepeats(false);
                  initialTimer.start();

              new Thread(new SwingTimerBug(0)).start();
          }

          SwingTimerBug(int delay) {
              m_delay = delay;
          }

          public void
          actionPerformed(ActionEvent event) {
              System.out.println("fired " + m_delay);
              if (m_delay == 100) {
                  SwingTimerBug longTimerNotify = new SwingTimerBug(120000);
                  Timer longTimer = new Timer(120000, longTimerNotify);
                  longTimer.setRepeats(false);
                  longTimer.start();

                  SwingTimerBug shortTimerNotify = new SwingTimerBug(1500);
                  Timer shortTimer = new Timer(1500, shortTimerNotify);
                  shortTimer.setRepeats(false);
                  shortTimer.start();
              }
          }
          
          public void
          run() {
              synchronized (this) {
                  try {
                      wait(200000);
                  } catch (InterruptedException e) {
                  }
              }
              System.exit(0);
          }
      }
      (Review ID: 54853)
      ======================================================================

            svioletsunw Scott Violet (Inactive)
            wleesunw William Lee (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: