Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-4294865

Deadlock caused by bug in java/awt/Window.java

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Fixed
    • Icon: P3 P3
    • 1.4.0
    • 1.3.0
    • client-libs
    • 2d
    • 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)
            

            hgodugusunw Harsha Godugu (Inactive)
            kryansunw Kevin Ryan (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: