-
Bug
-
Resolution: Fixed
-
P3
-
1.3.0
-
beta
-
generic
-
generic
at java.awt.EventDispatchThread.pumpOneEvent(EventDispatchThread.java:10
5)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:95)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:86)
"Finalizer" (TID:0x18995a0, sys_thread_t:0x773180, state:CW, native ID:0x10a
) prio=8
at java.lang.Object.wait(Native Method)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:112)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:127)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:166)
"Reference Handler" (TID:0x1899448, sys_thread_t:0x7726e0, state:CW, native
ID:0xfb) prio=10
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:424)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:114)
"Signal dispatcher" (TID:0x18993a8, sys_thread_t:0x771eb0, state:R, native I
D:0x83) prio=5
Monitor Cache Dump:
javax.swing.TimerQueue@18BA008/199D970: <unowned>
Waiting to be notified:
"TimerQueue" (0x843a20)
sun.awt.ScreenUpdater@18B9EF0/1962010: <unowned>
Waiting to be notified:
"Screen Updater" (0x84d580)
java.lang.Object@18AE9F8/1942380: owner "Drawing: AnnotationViewer1" (0x85d9
80) 1 entry
sun.awt.image.OffScreenImage@188E450/1C0C670: owner "Drawing: AnnotationView
er1" (0x85d980) 1 entry
Waiting to enter:
"AWT-EventQueue-0" (0x7d7c90)
java.lang.ref.ReferenceQueue$Lock@1899300/18CED98: <unowned>
Waiting to be notified:
"Finalizer" (0x773180)
java.awt.Component$AWTTreeLock@18A3CB8/19179F8: owner "AWT-EventQueue-0" (0x
7d7c90) 4 entries
Waiting to enter:
"Drawing: AnnotationViewer1" (0x85d980)
sun.awt.PostEventQueue@18B4118/1939400: <unowned>
Waiting to be notified:
"SunToolkit.PostEventQueue-0" (0x7d8180)
java.lang.ref.Reference$Lock@1899388/18CE8C0: <unowned>
Waiting to be notified:
"Reference Handler" (0x7726e0)
Registered Monitor Dump:
utf8 hash table: <unowned>
JNI pinning lock: <unowned>
JNI global reference lock: <unowned>
BinClass lock: <unowned>
Class linking lock: <unowned>
System class loader lock: <unowned>
Code rewrite lock: <unowned>
Heap lock: <unowned>
Monitor cache lock: owner "Signal dispatcher" (0x771eb0) 1 entry
Thread queue lock: owner "Signal dispatcher" (0x771eb0) 1 entry
Waiting to be notified:
"Thread-1" (0x85fd10)
Monitor registry: owner "Signal dispatcher" (0x771eb0) 1 entry
(Review ID: 98259)
======================================================================
Name: krT82822 Date: 11/26/99
java version "1.3beta"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.3beta-O)
Java(TM) HotSpot Client VM (build 1.3beta-O, mixed mode)
The included program demonstrates at least one bug in jdk 1.3beta Window.java
The program creates a JFrame and adds a JComponent to its
contentsPane. This component's paint(Graphics) method aquires a
synchronization monitor on an off-screen Image and then draws that
Image into the Graphics.
In the background a Drawing thread aquires the monitor on the
off-screen Image and modifies the Image. This process takes quite
awile because of the complexity of the calculations involved.
After the Image modifications are complete, an update() is issued
to the JFrame so that it will display the modified image.
Note that there has to be synchronized access to the off-screen
Image so that it is never displayed in an inconsistent state.
When the program is run under jdk 1.2.2 it works as one would
expect it to. The phrase "Hello World" is displayed in different
possitions in the JFrame.
java version "1.2.2"
Classic VM (build JDK-1.2.2-W, native threads, symcjit)
When the program is run under jdk 1.3beta, the program deadlocks
and does nothing. I consider this to be a bug in jdk 1.3beta.
java version "1.3beta"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.3beta-O)
Java(TM) HotSpot Client VM (build 1.3beta-O, mixed mode)
To make things easier for you here is a diff for
java/awt/Window.java that will fix the problem:
*** Window.java~ Thu Aug 19 10:49:52 1999
--- Window.java Wed Nov 24 12:43:08 1999
***************
*** 111,116 ****
--- 111,119 ----
private static final DebugHelper dbg = DebugHelper.create
(Container.class);
+ private static final Object graphicsConfigLock = new Object();
+
+
static {
/* ensure that the necessary native libraries are loaded */
Toolkit.loadLibraries();
***************
*** 1083,1089 ****
//NOTE: for multiscreen, this will need to take into account
//which screen the window is on/mostly on instead of returning
the
//default or constructor argument config.
! synchronized(getTreeLock()) {
if (graphicsConfig == null) {
graphicsConfig =
GraphicsEnvironment. getLocalGraphicsEnvironment().
--- 1086,1092 ----
//NOTE: for multiscreen, this will need to take into account
//which screen the window is on/mostly on instead of returning
the
//default or constructor argument config.
! synchronized(graphicsConfigLock) {
if (graphicsConfig == null) {
graphicsConfig =
GraphicsEnvironment. getLocalGraphicsEnvironment().
***************
*** 1099,1105 ****
* Called from the Toolkit thread, so NO CLIENT CODE.
*/
void resetGC() {
! synchronized(getTreeLock()) {
graphicsConfig = GraphicsEnvironment.
getLocalGraphicsEnvironment().
getDefaultScreenDevice().
--- 1102,1108 ----
* Called from the Toolkit thread, so NO CLIENT CODE.
*/
void resetGC() {
! synchronized(graphicsConfigLock) {
graphicsConfig = GraphicsEnvironment.
getLocalGraphicsEnvironment().
getDefaultScreenDevice().
You should also note that I submitted this same bug about a week ago and it was
assigned the internal review ID of: 98008
I got tired of the deadlock so I created this test program and then fixed my
local jdk as indicated above. With this fix all seems to function properly.
// ------------- Program follows
// Compile this program and then run it with no parameters.
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
import javax.swing.*;
public class Deadlock extends JFrame
{
Image buffer = null;
Random rand = new Random();
class DrawingThread implements Runnable
{
public void run()
{
while(true) {
if(buffer != null) {
synchronized(buffer) {
try {
// Long running calculation and drawing operation.
Thread.sleep(300L);
}
catch(InterruptedException ie){
}
Graphics g = buffer.getGraphics();
int w = buffer.getWidth(null);
int h = buffer.getHeight(null);
g.setClip(0, 0, w, h);
g.setColor(Color.white);
g.fillRect(0, 0, w, h);
g.setColor(Color.black);
int rx = 20 + (int)(rand.nextFloat() * (w - 40));
int ry = 20 + (int)(rand.nextFloat() * (h - 40));
System.out.println("x: " + rx + " y: " + ry);
g.drawString("Hello World!", rx, ry);
g.dispose();
}
Deadlock.this.repaint();
}
try {
Thread.sleep(1000L);
}
catch(InterruptedException ie){
}
}
}
}
class AntagonizerThread implements Runnable
{
public void run()
{
while(true) {
Deadlock.this.repaint();
try {
Thread.sleep(400L);
}
catch(InterruptedException ie){
}
}
}
}
static class DeadlockedContents extends JComponent
{
Image buffer;
DeadlockedContents(Image buffer)
{
this.buffer = buffer;
}
public void paint(Graphics g)
{
if(null != buffer) {
synchronized(buffer) {
g.drawImage(buffer, 0, 0, null);
}
}
}
}
public void startDrawingThread()
{
Thread drawingThread = new Thread(new DrawingThread(), "Drawer");
drawingThread.start();
Thread antagonizerThread = new Thread(new DrawingThread(), "Antagonizer");
antagonizerThread.start();
}
public static void main(String args[])
{
try {
Deadlock f = new Deadlock();
f.addWindowListener(new WindowListener(){
public void windowOpened(WindowEvent e) {return;}
public void windowClosing(WindowEvent e) {
e.getWindow().dispose();
System.exit(0);
}
public void windowClosed(WindowEvent e) {return;}
public void windowIconified(WindowEvent e) {return;}
public void windowDeiconified(WindowEvent e) {return;}
public void windowActivated(WindowEvent e) {return;}
public void windowDeactivated(WindowEvent e) {return;}
});
f.getContentPane().setLayout(new BorderLayout());
f.setSize(300, 300);
f.setResizable(false);
f.show();
f.buffer = f.createImage(300, 300);
DeadlockedContents dc = new DeadlockedContents(f.buffer);
f.getContentPane().add(dc, BorderLayout.CENTER);
f.invalidate();
f.validate();
f.startDrawingThread();
}
catch(Exception ex) {
ex.printStackTrace(System.err);
}
}
}
-------------------
(contents of previous report, # 98008:)
java version "1.3beta"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.3beta-O)
Java(TM) HotSpot Client VM (build 1.3beta-O, mixed mode)
Here is the scenario:
I have a program that has a background Thread that draws onto an off-screen
Image. Periodically that Image is painted onto an on-screen Component. In
order to assure that the Image is in a consistent state when drawn to the
screen I synchronize access to the Image using the Image itself as the
synchronization monitor.
When the AWT-EeventQueue paints the on-screen Component, it aquires the
Component.getTreeLock() monitor and then calls my paint() method which trys to
aquire the Image monitor.
When my background drawing thread is modifying the Image it aquires the Image
monitor then as part of its drawing does a getFontRenderContext() on the Image
Graphics. The problem is that getFontRenderContext() eventually calls
java.awt.Window.getGraphicsConfiguration() which tries to aquire the
Component.getTreeLock() monitor.
So we have AWT-EeventQueue aquiring monitors in the opposite order as the
background drawing Thread which leads to DEADLOCK!
Here is the source to java.awt.Window.getFontRenderContext():
/**
* This method returns the GraphicsConfiguration used by this Window.
*/
public GraphicsConfiguration getGraphicsConfiguration() {
//NOTE: for multiscreen, this will need to take into account
//which screen the window is on/mostly on instead of returning
the
//default or constructor argument config.
synchronized(getTreeLock()) {
if (graphicsConfig == null) {
graphicsConfig =
GraphicsEnvironment. getLocalGraphicsEnvironment().
getDefaultScreenDevice().
getDefaultConfiguration();
}
return graphicsConfig;
}
}
As you can see the getTreeLock() monitor is being used to protect
graphicsConfig, which is nice except that according to the documentation for
getTreeLock():
/**
* Gets the locking object for AWT component-tree and layout
* Gets this component's locking object (the object that owns the thread
* sychronization monitor) for AWT component-tree and layout
* operations.
* @return This component's locking object.
*/
it is supposed to be used to protect the component-tree and layout operations.
I can see why the getTreeLock() was used to protect graphicsConfig: It was
easy and did not require adding a monitor object to Window. However its use
has the indirect effect of causing Background drawing threads that need
synchronized access to off-screen Images to deadlock if they make certain
Graphics2D calls.
This is what I think should be done: In java.awt.Window create a separate
monitor object to protect the graphicsConfig. Since the graphicsConfig is not
related to the component-tree or layout operations, this should not create any
problems, it has the added benifit of not deadlocking code
Also this is a compatibility issue with version 1.1 because
java.awt.Graphics.drawString() ends up doing the same thing, so it would be
possible to write a program that runs fine under jdk 1.1 but would deadlock
under jdk 1.3 (and probably 1.2 also)
I know you guys like to see source code but my program is too large. I hope my
explanation is good enough, however for your viewing enjoyment I am including
the thread and monitor dump from my program that shows the deadlocked threads.
Pay special attention to "Drawing: AnnotationViewer1" and "AWT-EventQueue-0" as
they are the ones that are deadlocked.
Full thread dump Classic VM (1.3beta-O, native threads):
"TimerQueue" (TID:0x18ba010, sys_thread_t:0x843a20, state:CW, native ID:0xec
) prio=6
at java.lang.Object.wait(Native Method)
at javax.swing.TimerQueue.run(TimerQueue.java:236)
at java.lang.Thread.run(Thread.java:479)
"Thread-1" (TID:0x18b8540, sys_thread_t:0x85fd10, state:CW, native ID:0x12f)
prio=5
"Drawing: AnnotationViewer1" (TID:0x18be110, sys_thread_t:0x85d980, state:MW
, native ID:0xc7) prio=4
at java.awt.Window.getGraphicsConfiguration(Window.java:1086)
at sun.awt.image.BufferedImageGraphics2D.getDeviceConfiguration(Buffered
ImageGraphics2D.java:329)
at sun.java2d.SunGraphics2D.getFontRenderContext(SunGraphics2D.java:2354
)
at com.pixtran.image.annotation.PopupAnnotationItem.paintOverOrContains(
PopupAnnotationItem.java:356)
at com.pixtran.image.annotation.PopupAnnotationItem.rectPaintOver(PopupA
nnotationItem.java:254)
at com.pixtran.viewer.AnnotationViewer.paintImage(AnnotationViewer.java:
1076)
at com.pixtran.viewer.DrawingEngine.run(DrawingEngine.java:871)
at java.lang.Thread.run(Thread.java:479)
"Screen Updater" (TID:0x18b9ef0, sys_thread_t:0x84d580, state:CW, native ID:
0x104) prio=4
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:424)
at sun.awt.ScreenUpdater.nextEntry(ScreenUpdater.java:79)
at sun.awt.ScreenUpdater.run(ScreenUpdater.java:99)
"Image Animator 1" (TID:0x18a9140, sys_thread_t:0x82b180, state:CW, native I
D:0xee) prio=3
at java.lang.Thread.sleep(Native Method)
at sun.awt.image.GifFrame.dispose(GifImageDecoder.java:649)
at sun.awt.image.GifImageDecoder.readImage(GifImageDecoder.java:483)
at sun.awt.image.GifImageDecoder.produceImage(GifImageDecoder.java:227)
at sun.awt.image.InputStreamImageSource.doFetch(InputStreamImageSource.j
ava:261)
at sun.awt.image.ImageFetcher.fetchloop(ImageFetcher.java:221)
at sun.awt.image.ImageFetcher.run(ImageFetcher.java:189)
"AWT-Windows" (TID:0x18b5468, sys_thread_t:0x7dc950, state:R, native ID:0xf4
) prio=5
at sun.awt.windows.WToolkit.eventLoop(Native Method)
at sun.awt.windows.WToolkit.run(WToolkit.java:145)
at java.lang.Thread.run(Thread.java:479)
"SunToolkit.PostEventQueue-0" (TID:0x18b4118, sys_thread_t:0x7d8180, state:C
W, native ID:0x112) prio=5
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:424)
at sun.awt.PostEventQueue.run(SunToolkit.java:451)
"AWT-EventQueue-0" (TID:0x18b3e90, sys_thread_t:0x7d7c90, state:MW, native I
D:0xf8) prio=6
at com.pixtran.viewer.SimpleViewer.paint(SimpleViewer.java:1841)
at javax.swing.JComponent.paintChildren(JComponent.java:503)
at javax.swing.JComponent.paint(JComponent.java:714)
at javax.swing.JComponent.paintChildren(JComponent.java:491)
at javax.swing.JComponent.paint(JComponent.java:714)
at javax.swing.JComponent.paintChildren(JComponent.java:491)
at javax.swing.JComponent.paint(JComponent.java:714)
at javax.swing.JLayeredPane.paint(JLayeredPane.java:547)
at javax.swing.JComponent.paintChildren(JComponent.java:491)
at javax.swing.JComponent.paint(JComponent.java:685)
at java.awt.Container.paint(Container.java:970)
at javax.swing.JFrame.update(JFrame.java:303)
at sun.awt.RepaintArea.update(RepaintArea.java:343)
at sun.awt.windows.WComponentPeer.handleEvent(WComponentPeer.java:156)
at java.awt.Component.dispatchEventImpl(Component.java:2582)
at java.awt.Container.dispatchEventImpl(Container.java:1302)
at java.awt.Window.dispatchEventImpl(Window.java:854)
at java.awt.Component.dispatchEvent(Component.java:2443)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:302)
5)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:95)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:86)
"Finalizer" (TID:0x18995a0, sys_thread_t:0x773180, state:CW, native ID:0x10a
) prio=8
at java.lang.Object.wait(Native Method)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:112)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:127)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:166)
"Reference Handler" (TID:0x1899448, sys_thread_t:0x7726e0, state:CW, native
ID:0xfb) prio=10
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:424)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:114)
"Signal dispatcher" (TID:0x18993a8, sys_thread_t:0x771eb0, state:R, native I
D:0x83) prio=5
Monitor Cache Dump:
javax.swing.TimerQueue@18BA008/199D970: <unowned>
Waiting to be notified:
"TimerQueue" (0x843a20)
sun.awt.ScreenUpdater@18B9EF0/1962010: <unowned>
Waiting to be notified:
"Screen Updater" (0x84d580)
java.lang.Object@18AE9F8/1942380: owner "Drawing: AnnotationViewer1" (0x85d9
80) 1 entry
sun.awt.image.OffScreenImage@188E450/1C0C670: owner "Drawing: AnnotationView
er1" (0x85d980) 1 entry
Waiting to enter:
"AWT-EventQueue-0" (0x7d7c90)
java.lang.ref.ReferenceQueue$Lock@1899300/18CED98: <unowned>
Waiting to be notified:
"Finalizer" (0x773180)
java.awt.Component$AWTTreeLock@18A3CB8/19179F8: owner "AWT-EventQueue-0" (0x
7d7c90) 4 entries
Waiting to enter:
"Drawing: AnnotationViewer1" (0x85d980)
sun.awt.PostEventQueue@18B4118/1939400: <unowned>
Waiting to be notified:
"SunToolkit.PostEventQueue-0" (0x7d8180)
java.lang.ref.Reference$Lock@1899388/18CE8C0: <unowned>
Waiting to be notified:
"Reference Handler" (0x7726e0)
Registered Monitor Dump:
utf8 hash table: <unowned>
JNI pinning lock: <unowned>
JNI global reference lock: <unowned>
BinClass lock: <unowned>
Class linking lock: <unowned>
System class loader lock: <unowned>
Code rewrite lock: <unowned>
Heap lock: <unowned>
Monitor cache lock: owner "Signal dispatcher" (0x771eb0) 1 entry
Thread queue lock: owner "Signal dispatcher" (0x771eb0) 1 entry
Waiting to be notified:
"Thread-1" (0x85fd10)
Monitor registry: owner "Signal dispatcher" (0x771eb0) 1 entry
(Review ID: 98259)
======================================================================
Name: krT82822 Date: 11/26/99
java version "1.3beta"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.3beta-O)
Java(TM) HotSpot Client VM (build 1.3beta-O, mixed mode)
The included program demonstrates at least one bug in jdk 1.3beta Window.java
The program creates a JFrame and adds a JComponent to its
contentsPane. This component's paint(Graphics) method aquires a
synchronization monitor on an off-screen Image and then draws that
Image into the Graphics.
In the background a Drawing thread aquires the monitor on the
off-screen Image and modifies the Image. This process takes quite
awile because of the complexity of the calculations involved.
After the Image modifications are complete, an update() is issued
to the JFrame so that it will display the modified image.
Note that there has to be synchronized access to the off-screen
Image so that it is never displayed in an inconsistent state.
When the program is run under jdk 1.2.2 it works as one would
expect it to. The phrase "Hello World" is displayed in different
possitions in the JFrame.
java version "1.2.2"
Classic VM (build JDK-1.2.2-W, native threads, symcjit)
When the program is run under jdk 1.3beta, the program deadlocks
and does nothing. I consider this to be a bug in jdk 1.3beta.
java version "1.3beta"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.3beta-O)
Java(TM) HotSpot Client VM (build 1.3beta-O, mixed mode)
To make things easier for you here is a diff for
java/awt/Window.java that will fix the problem:
*** Window.java~ Thu Aug 19 10:49:52 1999
--- Window.java Wed Nov 24 12:43:08 1999
***************
*** 111,116 ****
--- 111,119 ----
private static final DebugHelper dbg = DebugHelper.create
(Container.class);
+ private static final Object graphicsConfigLock = new Object();
+
+
static {
/* ensure that the necessary native libraries are loaded */
Toolkit.loadLibraries();
***************
*** 1083,1089 ****
//NOTE: for multiscreen, this will need to take into account
//which screen the window is on/mostly on instead of returning
the
//default or constructor argument config.
! synchronized(getTreeLock()) {
if (graphicsConfig == null) {
graphicsConfig =
GraphicsEnvironment. getLocalGraphicsEnvironment().
--- 1086,1092 ----
//NOTE: for multiscreen, this will need to take into account
//which screen the window is on/mostly on instead of returning
the
//default or constructor argument config.
! synchronized(graphicsConfigLock) {
if (graphicsConfig == null) {
graphicsConfig =
GraphicsEnvironment. getLocalGraphicsEnvironment().
***************
*** 1099,1105 ****
* Called from the Toolkit thread, so NO CLIENT CODE.
*/
void resetGC() {
! synchronized(getTreeLock()) {
graphicsConfig = GraphicsEnvironment.
getLocalGraphicsEnvironment().
getDefaultScreenDevice().
--- 1102,1108 ----
* Called from the Toolkit thread, so NO CLIENT CODE.
*/
void resetGC() {
! synchronized(graphicsConfigLock) {
graphicsConfig = GraphicsEnvironment.
getLocalGraphicsEnvironment().
getDefaultScreenDevice().
You should also note that I submitted this same bug about a week ago and it was
assigned the internal review ID of: 98008
I got tired of the deadlock so I created this test program and then fixed my
local jdk as indicated above. With this fix all seems to function properly.
// ------------- Program follows
// Compile this program and then run it with no parameters.
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
import javax.swing.*;
public class Deadlock extends JFrame
{
Image buffer = null;
Random rand = new Random();
class DrawingThread implements Runnable
{
public void run()
{
while(true) {
if(buffer != null) {
synchronized(buffer) {
try {
// Long running calculation and drawing operation.
Thread.sleep(300L);
}
catch(InterruptedException ie){
}
Graphics g = buffer.getGraphics();
int w = buffer.getWidth(null);
int h = buffer.getHeight(null);
g.setClip(0, 0, w, h);
g.setColor(Color.white);
g.fillRect(0, 0, w, h);
g.setColor(Color.black);
int rx = 20 + (int)(rand.nextFloat() * (w - 40));
int ry = 20 + (int)(rand.nextFloat() * (h - 40));
System.out.println("x: " + rx + " y: " + ry);
g.drawString("Hello World!", rx, ry);
g.dispose();
}
Deadlock.this.repaint();
}
try {
Thread.sleep(1000L);
}
catch(InterruptedException ie){
}
}
}
}
class AntagonizerThread implements Runnable
{
public void run()
{
while(true) {
Deadlock.this.repaint();
try {
Thread.sleep(400L);
}
catch(InterruptedException ie){
}
}
}
}
static class DeadlockedContents extends JComponent
{
Image buffer;
DeadlockedContents(Image buffer)
{
this.buffer = buffer;
}
public void paint(Graphics g)
{
if(null != buffer) {
synchronized(buffer) {
g.drawImage(buffer, 0, 0, null);
}
}
}
}
public void startDrawingThread()
{
Thread drawingThread = new Thread(new DrawingThread(), "Drawer");
drawingThread.start();
Thread antagonizerThread = new Thread(new DrawingThread(), "Antagonizer");
antagonizerThread.start();
}
public static void main(String args[])
{
try {
Deadlock f = new Deadlock();
f.addWindowListener(new WindowListener(){
public void windowOpened(WindowEvent e) {return;}
public void windowClosing(WindowEvent e) {
e.getWindow().dispose();
System.exit(0);
}
public void windowClosed(WindowEvent e) {return;}
public void windowIconified(WindowEvent e) {return;}
public void windowDeiconified(WindowEvent e) {return;}
public void windowActivated(WindowEvent e) {return;}
public void windowDeactivated(WindowEvent e) {return;}
});
f.getContentPane().setLayout(new BorderLayout());
f.setSize(300, 300);
f.setResizable(false);
f.show();
f.buffer = f.createImage(300, 300);
DeadlockedContents dc = new DeadlockedContents(f.buffer);
f.getContentPane().add(dc, BorderLayout.CENTER);
f.invalidate();
f.validate();
f.startDrawingThread();
}
catch(Exception ex) {
ex.printStackTrace(System.err);
}
}
}
-------------------
(contents of previous report, # 98008:)
java version "1.3beta"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.3beta-O)
Java(TM) HotSpot Client VM (build 1.3beta-O, mixed mode)
Here is the scenario:
I have a program that has a background Thread that draws onto an off-screen
Image. Periodically that Image is painted onto an on-screen Component. In
order to assure that the Image is in a consistent state when drawn to the
screen I synchronize access to the Image using the Image itself as the
synchronization monitor.
When the AWT-EeventQueue paints the on-screen Component, it aquires the
Component.getTreeLock() monitor and then calls my paint() method which trys to
aquire the Image monitor.
When my background drawing thread is modifying the Image it aquires the Image
monitor then as part of its drawing does a getFontRenderContext() on the Image
Graphics. The problem is that getFontRenderContext() eventually calls
java.awt.Window.getGraphicsConfiguration() which tries to aquire the
Component.getTreeLock() monitor.
So we have AWT-EeventQueue aquiring monitors in the opposite order as the
background drawing Thread which leads to DEADLOCK!
Here is the source to java.awt.Window.getFontRenderContext():
/**
* This method returns the GraphicsConfiguration used by this Window.
*/
public GraphicsConfiguration getGraphicsConfiguration() {
//NOTE: for multiscreen, this will need to take into account
//which screen the window is on/mostly on instead of returning
the
//default or constructor argument config.
synchronized(getTreeLock()) {
if (graphicsConfig == null) {
graphicsConfig =
GraphicsEnvironment. getLocalGraphicsEnvironment().
getDefaultScreenDevice().
getDefaultConfiguration();
}
return graphicsConfig;
}
}
As you can see the getTreeLock() monitor is being used to protect
graphicsConfig, which is nice except that according to the documentation for
getTreeLock():
/**
* Gets the locking object for AWT component-tree and layout
* Gets this component's locking object (the object that owns the thread
* sychronization monitor) for AWT component-tree and layout
* operations.
* @return This component's locking object.
*/
it is supposed to be used to protect the component-tree and layout operations.
I can see why the getTreeLock() was used to protect graphicsConfig: It was
easy and did not require adding a monitor object to Window. However its use
has the indirect effect of causing Background drawing threads that need
synchronized access to off-screen Images to deadlock if they make certain
Graphics2D calls.
This is what I think should be done: In java.awt.Window create a separate
monitor object to protect the graphicsConfig. Since the graphicsConfig is not
related to the component-tree or layout operations, this should not create any
problems, it has the added benifit of not deadlocking code
Also this is a compatibility issue with version 1.1 because
java.awt.Graphics.drawString() ends up doing the same thing, so it would be
possible to write a program that runs fine under jdk 1.1 but would deadlock
under jdk 1.3 (and probably 1.2 also)
I know you guys like to see source code but my program is too large. I hope my
explanation is good enough, however for your viewing enjoyment I am including
the thread and monitor dump from my program that shows the deadlocked threads.
Pay special attention to "Drawing: AnnotationViewer1" and "AWT-EventQueue-0" as
they are the ones that are deadlocked.
Full thread dump Classic VM (1.3beta-O, native threads):
"TimerQueue" (TID:0x18ba010, sys_thread_t:0x843a20, state:CW, native ID:0xec
) prio=6
at java.lang.Object.wait(Native Method)
at javax.swing.TimerQueue.run(TimerQueue.java:236)
at java.lang.Thread.run(Thread.java:479)
"Thread-1" (TID:0x18b8540, sys_thread_t:0x85fd10, state:CW, native ID:0x12f)
prio=5
"Drawing: AnnotationViewer1" (TID:0x18be110, sys_thread_t:0x85d980, state:MW
, native ID:0xc7) prio=4
at java.awt.Window.getGraphicsConfiguration(Window.java:1086)
at sun.awt.image.BufferedImageGraphics2D.getDeviceConfiguration(Buffered
ImageGraphics2D.java:329)
at sun.java2d.SunGraphics2D.getFontRenderContext(SunGraphics2D.java:2354
)
at com.pixtran.image.annotation.PopupAnnotationItem.paintOverOrContains(
PopupAnnotationItem.java:356)
at com.pixtran.image.annotation.PopupAnnotationItem.rectPaintOver(PopupA
nnotationItem.java:254)
at com.pixtran.viewer.AnnotationViewer.paintImage(AnnotationViewer.java:
1076)
at com.pixtran.viewer.DrawingEngine.run(DrawingEngine.java:871)
at java.lang.Thread.run(Thread.java:479)
"Screen Updater" (TID:0x18b9ef0, sys_thread_t:0x84d580, state:CW, native ID:
0x104) prio=4
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:424)
at sun.awt.ScreenUpdater.nextEntry(ScreenUpdater.java:79)
at sun.awt.ScreenUpdater.run(ScreenUpdater.java:99)
"Image Animator 1" (TID:0x18a9140, sys_thread_t:0x82b180, state:CW, native I
D:0xee) prio=3
at java.lang.Thread.sleep(Native Method)
at sun.awt.image.GifFrame.dispose(GifImageDecoder.java:649)
at sun.awt.image.GifImageDecoder.readImage(GifImageDecoder.java:483)
at sun.awt.image.GifImageDecoder.produceImage(GifImageDecoder.java:227)
at sun.awt.image.InputStreamImageSource.doFetch(InputStreamImageSource.j
ava:261)
at sun.awt.image.ImageFetcher.fetchloop(ImageFetcher.java:221)
at sun.awt.image.ImageFetcher.run(ImageFetcher.java:189)
"AWT-Windows" (TID:0x18b5468, sys_thread_t:0x7dc950, state:R, native ID:0xf4
) prio=5
at sun.awt.windows.WToolkit.eventLoop(Native Method)
at sun.awt.windows.WToolkit.run(WToolkit.java:145)
at java.lang.Thread.run(Thread.java:479)
"SunToolkit.PostEventQueue-0" (TID:0x18b4118, sys_thread_t:0x7d8180, state:C
W, native ID:0x112) prio=5
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:424)
at sun.awt.PostEventQueue.run(SunToolkit.java:451)
"AWT-EventQueue-0" (TID:0x18b3e90, sys_thread_t:0x7d7c90, state:MW, native I
D:0xf8) prio=6
at com.pixtran.viewer.SimpleViewer.paint(SimpleViewer.java:1841)
at javax.swing.JComponent.paintChildren(JComponent.java:503)
at javax.swing.JComponent.paint(JComponent.java:714)
at javax.swing.JComponent.paintChildren(JComponent.java:491)
at javax.swing.JComponent.paint(JComponent.java:714)
at javax.swing.JComponent.paintChildren(JComponent.java:491)
at javax.swing.JComponent.paint(JComponent.java:714)
at javax.swing.JLayeredPane.paint(JLayeredPane.java:547)
at javax.swing.JComponent.paintChildren(JComponent.java:491)
at javax.swing.JComponent.paint(JComponent.java:685)
at java.awt.Container.paint(Container.java:970)
at javax.swing.JFrame.update(JFrame.java:303)
at sun.awt.RepaintArea.update(RepaintArea.java:343)
at sun.awt.windows.WComponentPeer.handleEvent(WComponentPeer.java:156)
at java.awt.Component.dispatchEventImpl(Component.java:2582)
at java.awt.Container.dispatchEventImpl(Container.java:1302)
at java.awt.Window.dispatchEventImpl(Window.java:854)
at java.awt.Component.dispatchEvent(Component.java:2443)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:302)