-
Bug
-
Resolution: Cannot Reproduce
-
P4
-
None
-
6
-
x86
-
windows_2000
FULL PRODUCT VERSION :
java version "1.6.0_02"
Java(TM) SE Runtime Environment (build 1.6.0_02-b05)
Java HotSpot(TM) Client VM (build 1.6.0_02-b05, mixed mode, sharing)
ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows 2000 [Version 5.00.2195]
A DESCRIPTION OF THE PROBLEM :
According to the JDK documentation, SwingWorker.isDone() returns true "if this task completed".
What exactly does "task completed" mean? Since a call to isDone() within an implementation of done() always returns true, I can see no possibility other than the return of the doInBackground method as the point in time when the task completes. The problem is that isDone() might return false even after the doInBackground method has returned.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the attached code. Depending on the performance of your system, you might have to play around with its time variables to reproduce the supposed bug.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The code should produce an endless sequence of output text blocks like
29193342 mySwingWorker: doInBackground returns
29193342 timerActionListener: mySwingWorker is done
or
29459700 timerActionListener: mySwingWorker is NOT done - stop timer
29459700 mySwingWorker: start timer
29459700 mySwingWorker: doInBackground returns
29459700 timerActionListener: mySwingWorker is done
ACTUAL -
The program stops once the supposed bug hits with an output text block like
3373112 timerActionListener: mySwingWorker is NOT done - stop timer
3373112 mySwingWorker: start timer
3373112 mySwingWorker: doInBackground returns
3373112 timerActionListener: mySwingWorker is NOT done - stop timer
or
12694833 mySwingWorker: doInBackground returns
12694833 timerActionListener: mySwingWorker is NOT done - stop timer
Since synchronizing on the lockObject establishes a happens-before relationship between the return of the doInBackground method in MySwingWorker and the calling of the isDone method in TimerActionListener,
the output shows that isDone() returns false after the task completes.
REPRODUCIBILITY :
This bug can be reproduced often.
---------- BEGIN SOURCE ----------
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Timer;
import javax.swing.SwingWorker;
class SwingWorkerBugDemo {
// Play around with these values to vary likelihood of bug encounter.
int timerDelayMillis = 20;
int sleepTimeMillis = 10;
Timer timer;
boolean doStartTimer;
MySwingWorker mySwingWorker;
Object lockObject = new Object();
SwingWorkerBugDemo() {
timer = new Timer(timerDelayMillis, new TimerActionListener());
timer.setInitialDelay(0);
mySwingWorker = new MySwingWorker();
mySwingWorker.execute();
timer.start();
}
public static void main(String[] args) {
new SwingWorkerBugDemo();
}
class TimerActionListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
synchronized (lockObject) {
if (mySwingWorker.isDone()) {
System.out.println(mySwingWorker.hashCode() + " timerActionListener: mySwingWorker is done\n");
mySwingWorker = new MySwingWorker();
mySwingWorker.execute();
} else {
System.out.println(mySwingWorker.hashCode() + " timerActionListener: mySwingWorker is NOT done - stop timer");
timer.stop();
doStartTimer = true;
}
}
}
}
class MySwingWorker extends SwingWorker<Object, Object> {
public Object doInBackground() {
try {
Thread.sleep(sleepTimeMillis);
} catch (InterruptedException ex) {
}
synchronized (lockObject) {
if (doStartTimer) {
System.out.println(this.hashCode() + " mySwingWorker: start timer");
timer.start();
doStartTimer = false;
}
System.out.println(this.hashCode() + " mySwingWorker: doInBackground returns");
return null;
}
}
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Don't use isDone() to find out if task completed.
java version "1.6.0_02"
Java(TM) SE Runtime Environment (build 1.6.0_02-b05)
Java HotSpot(TM) Client VM (build 1.6.0_02-b05, mixed mode, sharing)
ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows 2000 [Version 5.00.2195]
A DESCRIPTION OF THE PROBLEM :
According to the JDK documentation, SwingWorker.isDone() returns true "if this task completed".
What exactly does "task completed" mean? Since a call to isDone() within an implementation of done() always returns true, I can see no possibility other than the return of the doInBackground method as the point in time when the task completes. The problem is that isDone() might return false even after the doInBackground method has returned.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the attached code. Depending on the performance of your system, you might have to play around with its time variables to reproduce the supposed bug.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The code should produce an endless sequence of output text blocks like
29193342 mySwingWorker: doInBackground returns
29193342 timerActionListener: mySwingWorker is done
or
29459700 timerActionListener: mySwingWorker is NOT done - stop timer
29459700 mySwingWorker: start timer
29459700 mySwingWorker: doInBackground returns
29459700 timerActionListener: mySwingWorker is done
ACTUAL -
The program stops once the supposed bug hits with an output text block like
3373112 timerActionListener: mySwingWorker is NOT done - stop timer
3373112 mySwingWorker: start timer
3373112 mySwingWorker: doInBackground returns
3373112 timerActionListener: mySwingWorker is NOT done - stop timer
or
12694833 mySwingWorker: doInBackground returns
12694833 timerActionListener: mySwingWorker is NOT done - stop timer
Since synchronizing on the lockObject establishes a happens-before relationship between the return of the doInBackground method in MySwingWorker and the calling of the isDone method in TimerActionListener,
the output shows that isDone() returns false after the task completes.
REPRODUCIBILITY :
This bug can be reproduced often.
---------- BEGIN SOURCE ----------
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Timer;
import javax.swing.SwingWorker;
class SwingWorkerBugDemo {
// Play around with these values to vary likelihood of bug encounter.
int timerDelayMillis = 20;
int sleepTimeMillis = 10;
Timer timer;
boolean doStartTimer;
MySwingWorker mySwingWorker;
Object lockObject = new Object();
SwingWorkerBugDemo() {
timer = new Timer(timerDelayMillis, new TimerActionListener());
timer.setInitialDelay(0);
mySwingWorker = new MySwingWorker();
mySwingWorker.execute();
timer.start();
}
public static void main(String[] args) {
new SwingWorkerBugDemo();
}
class TimerActionListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
synchronized (lockObject) {
if (mySwingWorker.isDone()) {
System.out.println(mySwingWorker.hashCode() + " timerActionListener: mySwingWorker is done\n");
mySwingWorker = new MySwingWorker();
mySwingWorker.execute();
} else {
System.out.println(mySwingWorker.hashCode() + " timerActionListener: mySwingWorker is NOT done - stop timer");
timer.stop();
doStartTimer = true;
}
}
}
}
class MySwingWorker extends SwingWorker<Object, Object> {
public Object doInBackground() {
try {
Thread.sleep(sleepTimeMillis);
} catch (InterruptedException ex) {
}
synchronized (lockObject) {
if (doStartTimer) {
System.out.println(this.hashCode() + " mySwingWorker: start timer");
timer.start();
doStartTimer = false;
}
System.out.println(this.hashCode() + " mySwingWorker: doInBackground returns");
return null;
}
}
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Don't use isDone() to find out if task completed.