-
Bug
-
Resolution: Duplicate
-
P3
-
None
-
1.2.0
-
x86
-
windows_nt
Name: rm29839 Date: 06/01/98
In Swing 1.0.2, there appears to be a bug in either
SwingUtilities.isEventDispatchThread() or
JComponent.revalidate().
The bug can lead to deadlock (frozen, unresponsive,
gray windows) when it interacts with read and
write locking in AbstractDocument.
First look at JComponent.revalidate(). Here is
the code from JComponent.java, near line 2903:
public void revalidate() {
if (SwingUtilities.isEventDispatchThread()) {
invalidate();
RepaintManager.currentManager(this).addInvalidComponent(this);
}
else {
Runnable callRevalidate = new Runnable() {
public void run() {
revalidate();
}
};
SwingUtilities.invokeLater(callRevalidate);
}
}
Apparently, revalidate is trying to determine
whether it is running on the currently active AWT
event-handling thread.
(If it is, it will initiate window invalidation
directly. Otherwise (if it is not running on the
event-handling thread), it takes special measure
to run window invalidation on the event-handling
thread.)
To find out if it is running on the event-handling
thread or not, revalidate calls
SwingUtilities.isEventDispatchThread.
However, that call does not determine whether the
current thread is the _active_ dispatch thread;
it only checks whether the thread is _a_ dispatch
thread. (Explained below.)
If a modal dialog was shown from the original
dispatch thread, there will be a proxy dispatch
thread that takes over while the original dispatch
thread is suspended.
(The proxy dispatch thread is the one created in
Dialog.show():
dt = new EventDispatchThread("AWT-Dispatch-Proxy",
Toolkit.getEventQueue());
)
The call be revalidate to
SwingUtilities.isEventDispatchThread checks only
whether the current thread is _a_ dispatch thread.
It does not check whether any other dispatch
thread exists.
Specifically, it checks only the class of the
thread object; it doesn't check any indication
of which dispatch thread is currently active, or
how many are active.
Here's the code:
/**
* @return true if the current thread is an AWT event dispatching thread.
*/
public static boolean isEventDispatchThread()
{
Thread currentThread = Thread.currentThread();
/* The first time we're called on what appears to be the event
* dispatching thread, we stash the threads class in
* eventDispatchThreadClass. Subsequently we effectively
* return eventDispatchThreadClass instanceof Thread.currentThread().
*/
if (eventDispatchThreadClass == null) {
Class currentThreadClass = currentThread.getClass();
/* This test is a crock. It's known to work on all of the popular
* JDK1.1 implementations available as of January 1998.
*/
if((currentThreadClass.getName().indexOf("EventDispatchThread") >= 0) ||
(currentThreadClass.getName().indexOf("JMEventQueue") >= 0)) {
eventDispatchThreadClass = currentThreadClass;
return true;
}
else {
return false;
}
}
return eventDispatchThreadClass.isInstance(currentThread);
}
(Note: Below here I'm highlighting "_re_validate"
vs. "_in_validate" to try to avoid confusion
between the two.)
If JComponent._re_validate is called from the
original dispatch thread while a proxy dispatch
thread is active, _re_validate won't cause
_in_validate to be invoked on the proxy dispatch
thread; it will begin to execute it directly on
its own thread, which is the original dispatch
thread.
If the proxy dispatch thread is active (painting
an apparently taking locks), this can lead to
deadlock.
I think that isEventDispatchThread (or revalidate)
should check whether the current thread is the
_active_ dispatch thread, not just _a_ dispatch
thread.
Maybe you need to more formally define whether
there is one active thread, and then define whether
isEventDispatchThread is supposed to indicate
whether you're on _a_ dispatch thread or _the_
active one.
Here's the deadlock situation I encountered and
what I think happened. It involved a modal dialog
and the read and write locking of AbstractDocument.
I ran my application, which created a top-level
window with some buttons.
I clicked on a button in that top-level window.
The button listener code created a dialog, set
it to model, and showed it. This blocked the
original dispatch thread, right after it started
up a proxy dispatch thread.
I clicked on a button in the modal dialog to
cause it to dispose itself. This action took
place on the proxy dispatch thread. It also
unblocked the original dispatch thread.
The original dispatch thread called stopDispatching
so that the proxy dispatch thread would stop the
next time it checked its doDispatch flag, and
then continued with the original button listener
code.
Apparently, before doDispatch was cleared by
the original dispatch thread, the proxy dispatch
thread had already gotten started servicing some
repaint event (as evidenced by a multi-thread
stack trace (the thing labeled "Full stack dump")
taken after things locked up).
Back on the newly-resumed original dispatch thread,
my button listener code called the insertString
method of (my subclass of) an AbstractDocument.
That method called writeLock, modified the data
structures, and then began notifying listeners,
including the renderers (e.g., BoxView,
ParagraphView).
Meanwhile, the proxy dispatch thread had started
to repaint the top-level window. (This seemed to
happen whether or not the modal dialog box was
obscuring any part of the top-level window at
the time it was dismissed.)
Deep in a chain of calls to paint and paintChildren,
the proxy thread called AbstractDocument.render,
which called AbstractDocument.readLock, which
blocked because the original dispatch thread had
the write lock. Now the proxy dispatch thread
was blocked, waiting on the AbstractDocument lock.
Apparently, it was also holding some lock(s)
related to UI components.
Back in the original dispatch thread, some
renderer called JComponent.revalidate (from deep
under the document-modification code that grabbed
the write lock). Revalidate apparently tried
invoking _in_validate directly (on the same
thread (the original dispatch thread)).
Apparently, _in_validate blocked on the orignal
dispatch thread because the proxy dispatch
thread was in the middle of painting, and had
lock something (UI components?). Of course,
the proxy dispatch thread had blocked because it
couldn't get a read lock for the document data
to repaint.
The window was all gray and the application was
unresponsive. The full stack trace that I got
was taken from this state.
If _re_validate had invoked _in_validate on the
currently active dispatch thread (the proxy
thread), it would not have blocked the original
dispatch thread, so it could have finished up
nested calls and gotten back to the document-
modification method that had grabbed the write
lock. That method would have called writeUnlock
and released the lock.
The proxy dispatch thread would no longer be
blocked trying to read the document, so it would
resume to read the data and paint the screen.
(I assume it would then process the specially-
invoked call to _in_validate.)
I can't send a test case for this, because the
problem is intermittent. On my main machine, it
appears only occasionally. On another machine,
it usually happens (but not always). It would
take much to long to trim down my application
given that the problem shows up only intermittently.
The problem occurs on your JVM 1.1.6 (and 1.1.5),
and also occurs (also intermittently) using
whatever JVM is in Symantec's Cafe.
However, here is the stack dump that I got.
(I don't recall how it got chopped after
"Registered Mo".)
Full thread dump:
"AWT-Dispatch-Proxy" (TID:0xf73340, sys_thread_t:0x8c2d00, Win32ID:0x218, state:CW) prio=5
java.lang.Object.wait(Object.java:315)
com.sun.java.swing.text.AbstractDocument.readLock(AbstractDocument.java:683)
com.sun.java.swing.text.AbstractDocument.render(AbstractDocument.java:250)
com.sun.java.swing.plaf.basic.BasicTextUI.paint(BasicTextUI.java:504)
com.sun.java.swing.plaf.ComponentUI.update(ComponentUI.java:47)
com.sun.java.swing.JComponent.paintComponent(JComponent.java:322)
com.sun.java.swing.JComponent.paint(JComponent.java:534)
com.sun.java.swing.JComponent.paintChildren(JComponent.java:382)
com.sun.java.swing.JComponent.paint(JComponent.java:537)
com.sun.java.swing.JComponent.paintChildren(JComponent.java:382)
com.sun.java.swing.JComponent.paint(JComponent.java:537)
com.sun.java.swing.JComponent.paintChildren(JComponent.java:382)
com.sun.java.swing.JComponent.paint(JComponent.java:537)
com.sun.java.swing.JComponent.paintChildren(JComponent.java:382)
com.sun.java.swing.JComponent.paint(JComponent.java:537)
com.sun.java.swing.JLayeredPane.paint(JLayeredPane.java:555)
com.sun.java.swing.JComponent.paintChildren(JComponent.java:382)
com.sun.java.swing.JComponent.paint(JComponent.java:522)
java.awt.Container.paint(Container.java:702)
java.awt.Component.dispatchEventImpl(Component.java:1723)
"TimerQueue" (TID:0xf7f1b8, sys_thread_t:0x8c7b20, Win32ID:0x1f3, state:CW) prio=5
com.sun.java.swing.TimerQueue.run(TimerQueue.java:210)
java.lang.Thread.run(Thread.java:474)
"Screen Updater" (TID:0xf86658, sys_thread_t:0x8c11c0, Win32ID:0x1d6, state:CW) prio=4
java.lang.Object.wait(Object.java:315)
sun.awt.ScreenUpdater.nextEntry(ScreenUpdater.java:78)
sun.awt.ScreenUpdater.run(ScreenUpdater.java:98)
"AWT-Windows" (TID:0xf654c0, sys_thread_t:0x890b00, Win32ID:0x1d8, state:R) prio=5
sun.awt.windows.WToolkit.run(WToolkit.java:106)
java.lang.Thread.run(Thread.java:474)
"AWT-EventQueue-0" (TID:0xf652e0, sys_thread_t:0x8897d0, Win32ID:0x217, state:MW) prio=5
java.awt.Component.invalidate(Component.java:1126)
java.awt.Container.invalidate(Container.java:485)
com.sun.java.swing.JComponent.revalidate(JComponent.java:2904)
com.sun.java.swing.plaf.basic.BasicTextUI$RootView.preferenceChanged(BasicTextUI.java:788)
com.sun.java.swing.text.View.preferenceChanged(View.java:84)
com.sun.java.swing.text.BoxView.preferenceChanged(BoxView.java:119)
com.sun.java.swing.text.View.preferenceChanged(View.java:84)
com.sun.java.swing.text.BoxView.preferenceChanged(BoxView.java:119)
com.sun.java.swing.text.ParagraphView.insertUpdate(ParagraphView.java:642)
com.sun.java.swing.text.BoxView.insertUpdate(BoxView.java:327)
com.sun.java.swing.plaf.basic.BasicTextUI$RootView.insertUpdate(BasicTextUI.java:908)
com.sun.java.swing.plaf.basic.BasicTextUI$UpdateHandler.insertUpdate(BasicTextUI.java:1124)
com.sun.java.swing.text.AbstractDocument.fireInsertUpdate(AbstractDocument.java:139)
gendoc.core.data.MyTemplate.insertFieldInsertionPoint(MyTemplate.java:400)
gendoc.core.render.MyTemplateEditorKit$InsertFieldInsertionPointAction.actionPerformed(MyTemplateEditorKit.java:258)
gendoc.core.render.TemplateEditorPane.insertFieldInsertionPoint(TemplateEditorPane.java:144)
gendoc.driver.TemplateEditorWindow.doInsertField(TemplateEditorWindow.java:703)
gendoc.driver.TemplateEditorWindow.access$4(TemplateEditorWindow.java:665)
gendoc.driver.TemplateEditorWindow$8.actionPerformed(TemplateEditorWindow.java:350)
com.sun.java.swing.AbstractButton.fireActionPerformed(AbstractButton.java:861)
"Finalizer thread" (TID:0xf60088, sys_thread_t:0x884380, Win32ID:0x208, state:CW) prio=2
"main" (TID:0xf600b0, sys_thread_t:0x880b90, Win32ID:0x201, state:CW) prio=5
Monitor Cache Dump:
java.lang.Object@F63D68/FACC18: owner "AWT-Dispatch-Proxy" (0x8c2d00, 5 entries)
gendoc.core.data.MyTemplate@F6EE70/FF31B0: <unowned>
Waiters: 1
com.sun.java.swing.TimerQueue@F7F198/FF56E0: <unowned>
Waiters: 1
sun.awt.ScreenUpdater@F86658/FB7F10: <unowned>
Waiters: 1
Registered Mo
(Review ID: 32250)
======================================================================
- duplicates
-
JDK-4122683 hiding modal Dialog on NT can deadlock, freezing GUI
- Closed