-
Bug
-
Resolution: Fixed
-
P3
-
1.1.7
-
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)
======================================================================