JDK-6623943 : javax.swing.TimerQueue's thread occasionally fails to start
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 7
  • Priority: P2
  • Status: Closed
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2007-10-31
  • Updated: 2011-03-08
  • Resolved: 2011-03-07
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 7 Other
7 b38Fixed OpenJDK6Fixed
Related Reports
Relates :  
Description
The IRIS demo application developed for JavaOne 2007 (http://iris.dev.java.net/ , http://swinglabs.org/iris/) exhibits a problem where the application occasionally fails to start successfully; it will not load any users' albums nor perform many other actions.

The symptom is lost property change notifications deep in the SwingWorker code: changes to the "state" bound property were not propagating out, causing the Swing Application Framework to fail to notify the "succeeded" / "failed" listeners on its Tasks, causing Iris to fail to update the web page after loading a user's albums from Flickr.

The root cause is the use of a one-shot Swing timer in the DoSubmitAccumulativeRunnable inner class of SwingWorker. For some reason, occasionally the first javax.swing.Timer created and started in the system will fail to fire. This will cause all subsequent calls to SwingWorkerPropertyChangeSupport.firePropertyChange() to be enqueued on a list that will never be processed.

It seems that if the first Timer successfully fires, then all subsequent ones will.

Commenting out the definition of DoSubmitAccumulativeRunnable.submit(), using the superclass's version which calls SwingUtilities.invokeLater(), works around the problem.

Working with ###@###.### indicates that the problem appears to be that in some circumstances the per-AppContext TimerQueue thread fails to start.

The problem is related to the changes to the Swing Timer and TimerQueue from 5053997. Reverting these classes in a JDK 7 workspace to the pre-5053997 versions (as well as dependent classes) works around the problem.

Comments
EVALUATION suggested fix: =================== # HG changeset patch # User idk # Date 1214248897 14400 # Node ID fc09152d5cf67bb2fece572cdd074734fd9bd7ac # Parent fa7147a26cd25dbba698753ca524a680db6f6e43 6623943: javax.swing.TimerQueue's thread occasionally fails to start Reviewed-by: alexp diff --git a/src/share/classes/javax/swing/JApplet.java b/src/share/classes/javax/swing/JApplet.java --- a/src/share/classes/javax/swing/JApplet.java +++ b/src/share/classes/javax/swing/JApplet.java @@ -131,10 +131,7 @@ // Check the timerQ and restart if necessary. TimerQueue q = TimerQueue.sharedInstance(); if(q != null) { - synchronized(q) { - if(!q.running) - q.start(); - } + q.startIfNeeded(); } /* Workaround for bug 4155072. The shared double buffer image diff --git a/src/share/classes/javax/swing/TimerQueue.java b/src/share/classes/javax/swing/TimerQueue.java --- a/src/share/classes/javax/swing/TimerQueue.java +++ b/src/share/classes/javax/swing/TimerQueue.java @@ -31,6 +31,7 @@ import java.util.*; import java.util.concurrent.*; +import java.util.concurrent.locks.*; import java.util.concurrent.atomic.AtomicLong; import sun.awt.AppContext; @@ -52,7 +53,8 @@ new StringBuffer("TimerQueue.expiredTimersKey"); private final DelayQueue<DelayedTimer> queue; - volatile boolean running; + private volatile boolean running; + private final Lock runningLock; /* Lock object used in place of class object for synchronization. * (4187686) @@ -69,7 +71,8 @@ super(); queue = new DelayQueue<DelayedTimer>(); // Now start the TimerQueue thread. - start(); + runningLock = new ReentrantLock(); + startIfNeeded(); } @@ -87,32 +90,29 @@ } - synchronized void start() { - if (running) { - throw new RuntimeException("Can't start a TimerQueue " + - "that is already running"); - } - else { - final ThreadGroup threadGroup = - AppContext.getAppContext().getThreadGroup(); - java.security.AccessController.doPrivileged( - new java.security.PrivilegedAction() { - public Object run() { - Thread timerThread = new Thread(threadGroup, TimerQueue.this, - "TimerQueue"); - timerThread.setDaemon(true); - timerThread.setPriority(Thread.NORM_PRIORITY); - timerThread.start(); - return null; - } - }); - running = true; + void startIfNeeded() { + if (! running) { + runningLock.lock(); + try { + final ThreadGroup threadGroup = + AppContext.getAppContext().getThreadGroup(); + java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public Object run() { + Thread timerThread = new Thread(threadGroup, TimerQueue.this, + "TimerQueue"); + timerThread.setDaemon(true); + timerThread.setPriority(Thread.NORM_PRIORITY); + timerThread.start(); + return null; + } + }); + running = true; + } finally { + runningLock.unlock(); + } } } - - synchronized void stop() { - running = false; - } void addTimer(Timer timer, long delayMillis) { timer.getLock().lock(); @@ -164,6 +164,7 @@ public void run() { + runningLock.lock(); try { while (running) { try { @@ -195,14 +196,14 @@ } } catch (ThreadDeath td) { - synchronized (this) { - running = false; - // Mark all the timers we contain as not being queued. - for (DelayedTimer delayedTimer : queue) { - delayedTimer.getTimer().cancelEvent(); - } - throw td; + // Mark all the timers we contain as not being queued. + for (DelayedTimer delayedTimer : queue) { + delayedTimer.getTimer().cancelEvent(); } + throw td; + } finally { + running = false; + runningLock.unlock(); } } ===================
23-06-2008

EVALUATION This bug is a regression introduced by the fix for 5053997 [Swing Timer cannot handle multiple timers effectively] TimerQueue.start method starts the TimerQueue's thread and sets 'running' property to true. TimerQueue.run method is runing in the TimerQueue's thread until 'running' property is false. Before the fix for 5053997 start and run methods were synchronized. So 'running' property were set to true before TimerQueue.run method was executed. After the fix for 5053997 there is no synchronization there and thus TimerQueue.run might be executed before 'running' is set to true and in this case it will exit immediately.
12-11-2007