JDK-6903354 : deadlock involving Component.show & SunToolkit.getImageFromHash
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.awt
  • Affected Version: 6u18
  • Priority: P2
  • Status: Closed
  • Resolution: Won't Fix
  • OS: generic
  • CPU: generic
  • Submitted: 2009-11-20
  • Updated: 2013-01-29
  • Resolved: 2013-01-29
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
7Fixed
Related Reports
Relates :  
Description
The test below reproduces the deadlock.
Before trying it, create an image and put it to some https server.

1. Put the URL of the image into the test.
2. Compile and run it with javaws.
3. Press the button in the frame.
4. A security dialog will popup and the test will deadlock.

======================================================================================
package simpleimagetest;

import java.awt.event.*;
import java.net.URL;
import javax.swing.*;

public class Main {

    static String imgLocation;

    public static void main(String[] args) {
        imgLocation = "https://*";  // substitute '*' with a valid URL of an image
        if (args.length > 0) {
            imgLocation = args[0];
        }

        System.out.println("Image: " + imgLocation);

        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                initUI();
            }
        });

    }

    private static void initUI() {
        JButton btn = new JButton("Just do it!");
        btn.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e) {
                try {
                    ImageIcon i = new ImageIcon(new URL(imgLocation));
                } catch (Throwable ex) {}
            }
        });

        JFrame f = new JFrame(imgLocation);
        f.add(btn);
        f.pack();
        f.setVisible(true);
    }
}
======================================================================================

Here's the stacktrace:

"AWT-EventQueue-0":
    at javax.swing.JComponent.paintChildren(Unknown Source)
    - waiting to lock <0x22ec8578> (a java.awt.Component$AWTTreeLock)
    at javax.swing.JComponent.paintToOffscreen(Unknown Source)
    at javax.swing.BufferStrategyPaintManager.paint(Unknown Source)
    at javax.swing.RepaintManager.paint(Unknown Source)
    at javax.swing.JComponent.paint(Unknown Source)
    at java.awt.GraphicsCallback$PaintCallback.run(Unknown Source)
    at sun.awt.SunGraphicsCallback.runOneComponent(Unknown Source)
    at sun.awt.SunGraphicsCallback.runComponents(Unknown Source)
    at java.awt.Container.paint(Unknown Source)
    at java.awt.Window.paint(Unknown Source)
    at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)
    at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)
    at javax.swing.RepaintManager.seqPaintDirtyRegions(Unknown Source)
    at javax.swing.SystemEventQueueUtilities$ComponentWorkRequest.run(Unknown Source)
    at java.awt.event.InvocationEvent.dispatch(Unknown Source)
    at java.awt.EventQueue.dispatchEvent(Unknown Source)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
    at java.awt.Dialog$1.run(Unknown Source)
    at java.awt.Dialog$3.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.awt.Dialog.show(Unknown Source)
    at java.awt.Component.show(Unknown Source)
    at java.awt.Component.setVisible(Unknown Source)
    at java.awt.Window.setVisible(Unknown Source)
    at java.awt.Dialog.setVisible(Unknown Source)
    at com.sun.javaws.ui.JavawsSysRun.delegateFromEDT(Unknown Source)
    at com.sun.javaws.ui.JavawsSysRun.delegate(Unknown Source)
    at com.sun.deploy.util.DeploySysRun.execute(Unknown Source)
    at com.sun.deploy.util.DeploySysRun$1.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.deploy.util.DeploySysRun.executePrivileged(Unknown Source)
    at com.sun.deploy.ui.UIFactory.showApiDialog(Unknown Source)
    at com.sun.jnlp.ApiDialog.askUser(Unknown Source)
    at com.sun.jnlp.ApiDialog.askUser(Unknown Source)
    at com.sun.jnlp.ApiDialog.askConnect(Unknown Source)
    at com.sun.javaws.security.JavaWebStartSecurity.checkConnect(Unknown Source)
    at sun.awt.SunToolkit.getImageFromHash(Unknown Source)
    - locked <0x2b9a4a10> (a java.lang.Class for sun.awt.SunToolkit)
    at sun.awt.SunToolkit.getImage(Unknown Source)
    at javax.swing.ImageIcon.<init>(Unknown Source)
    at javax.swing.ImageIcon.<init>(Unknown Source)
    at simpleimagetest.Main$2.actionPerformed(Main.java:49)
    at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
    at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
    at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
    at javax.swing.DefaultButtonModel.setPressed(Unknown Source)
    at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)
    at java.awt.Component.processMouseEvent(Unknown Source)
    at javax.swing.JComponent.processMouseEvent(Unknown Source)
    at java.awt.Component.processEvent(Unknown Source)
    at java.awt.Container.processEvent(Unknown Source)
    at java.awt.Component.dispatchEventImpl(Unknown Source)
    at java.awt.Container.dispatchEventImpl(Unknown Source)
    at java.awt.Component.dispatchEvent(Unknown Source)
    at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
    at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
    at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
    at java.awt.Container.dispatchEventImpl(Unknown Source)
    at java.awt.Window.dispatchEventImpl(Unknown Source)
    at java.awt.Component.dispatchEvent(Unknown Source)
    at java.awt.EventQueue.dispatchEvent(Unknown Source)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.run(Unknown Source)
"Javaws Secure Thread":
    at sun.awt.SunToolkit.getSunAwtDisableMixing(Unknown Source)
    - waiting to lock <0x2b9a4a10> (a java.lang.Class for sun.awt.SunToolkit)
    at java.awt.Component.isMixingNeeded(Unknown Source)
    at java.awt.Component.mixOnHiding(Unknown Source)
    - locked <0x22ec8578> (a java.awt.Component$AWTTreeLock)
    at java.awt.Component.hide(Unknown Source)
    - locked <0x22ec8578> (a java.awt.Component$AWTTreeLock)
    at java.awt.Component.show(Unknown Source)
    at java.awt.Component.setVisible(Unknown Source)
    at javax.swing.JComponent.setVisible(Unknown Source)
    at javax.swing.JRootPane.createGlassPane(Unknown Source)
    at javax.swing.JRootPane.<init>(Unknown Source)
    at javax.swing.JDialog.createRootPane(Unknown Source)
    at javax.swing.JDialog.dialogInit(Unknown Source)
    at javax.swing.JDialog.<init>(Unknown Source)
    at javax.swing.JDialog.<init>(Unknown Source)
    at com.sun.deploy.ui.DialogTemplate.<init>(Unknown Source)
    at com.sun.deploy.ui.UIFactory$9.execute(Unknown Source)
    at com.sun.javaws.ui.JavawsSysRun$SecureThread.doWork(Unknown Source)
    at com.sun.javaws.ui.JavawsSysRun$SecureThread.run(Unknown Source)
    - locked <0x22ec0f80> (a java.lang.Object)

Comments
SUGGESTED FIX It's worth making imgCache final.
03-12-2009

SUGGESTED FIX @@ -823,11 +823,11 @@ return AccessController.doPrivileged(new GetBooleanAction("sun.awt.erasebackgroundonresize")); } static SoftCache imgCache = new SoftCache(); - static synchronized Image getImageFromHash(Toolkit tk, URL url) { + static Image getImageFromHash(Toolkit tk, URL url) { SecurityManager sm = System.getSecurityManager(); if (sm != null) { try { java.security.Permission perm = url.openConnection().getPermission(); @@ -851,37 +851,40 @@ } } catch (java.io.IOException ioe) { sm.checkConnect(url.getHost(), url.getPort()); } } + synchronized (imgCache) { Image img = (Image)imgCache.get(url); if (img == null) { try { img = tk.createImage(new URLImageSource(url)); imgCache.put(url, img); } catch (Exception e) { } } return img; } + } - static synchronized Image getImageFromHash(Toolkit tk, - String filename) { + static Image getImageFromHash(Toolkit tk, String filename) { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkRead(filename); } + synchronized (imgCache) { Image img = (Image)imgCache.get(filename); if (img == null) { try { img = tk.createImage(new FileImageSource(filename)); imgCache.put(filename, img); } catch (Exception e) { } } return img; } + } public Image getImage(String filename) { return getImageFromHash(this, filename); }
20-11-2009

EVALUATION The reason of the deadlock is that SunToolkit.class and AWTTreeLock locks are taken in different order. The SunToolkit.getImageFromHash(..) methods are synchronized by SunToolkit.class object. The scope of the synchronization is too wide, there's no need to include the security check in it. Actually, I believe the synchronization was aimed at synchronizing access to the image cache only. So, the scope should be narrowed down. This will prevent us from locking SunToolkit.class before locking AWTTreeLock (that happens in the depths of the deploy security-related code).
20-11-2009