Deadlock in Applet with HTMLDocument

Leave a comment

September 16, 2011 by huionn

Today I encountered a weird problem where my applet crashed/hanged intermittently during initialization. My applet is basically a HTML editor with multiple HTMLDocuments retrieved over network. The contents can be edited with undo/redo support.

As the Java console crashed together with my applet, I tried to traced by adding logs (as I found out later, those logs are misleading – there is slight delay with the output to Java console. As the console crashes, the logs did not show the actual point of program execution). It was fruitless effort as the logs led me nowhere.

Luckily I know jvisualvm – Java Virtual Machine Monitoring, Troubleshooting, and Profiling Tool. With its thread dump, I managed to figure out the cause.

"Thread-33" prio=4 tid=0x00cad800 nid=0x2f58 waiting for monitor entry [0x0443f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
	at java.awt.Component.enable(Unknown Source)
	- waiting to lock <0x28012738> (a java.awt.Component$AWTTreeLock)
	at javax.swing.JComponent.enable(Unknown Source)
	at java.awt.Component.enable(Unknown Source)
	at java.awt.Component.setEnabled(Unknown Source)
	at javax.swing.JComponent.setEnabled(Unknown Source)
	at javax.swing.AbstractButton.setEnabled(Unknown Source)
	at javax.swing.JMenuItem.setEnabled(Unknown Source)
	at com.fsc.client.label.LabelEditor$ActionChangedListener.propertyChange(LabelEditor.java:612)
	at java.beans.PropertyChangeSupport.firePropertyChange(Unknown Source)
	at javax.swing.event.SwingPropertyChangeSupport.firePropertyChange(Unknown Source)
	at java.beans.PropertyChangeSupport.firePropertyChange(Unknown Source)
	at javax.swing.AbstractAction.firePropertyChange(Unknown Source)
	at javax.swing.AbstractAction.setEnabled(Unknown Source)
	at com.fsc.client.label.actions.UndoAction.update(UndoAction.java:39)
	at com.fsc.client.label.actions.UndoHandler.undoableEditHappened(UndoHandler.java:26)
	at javax.swing.text.AbstractDocument.fireUndoableEditUpdate(Unknown Source)
	at javax.swing.text.html.HTMLDocument.fireUndoableEditUpdate(Unknown Source)
	at javax.swing.text.DefaultStyledDocument.insert(Unknown Source)
	at javax.swing.text.html.HTMLDocument.insert(Unknown Source)
	at javax.swing.text.html.HTMLDocument$HTMLReader.flushBuffer(Unknown Source)
	at javax.swing.text.html.HTMLDocument$HTMLReader.flush(Unknown Source)
	at javax.swing.text.html.HTMLEditorKit.read(Unknown Source)
	at javax.swing.JEditorPane.read(Unknown Source)
	at javax.swing.JEditorPane$PageLoader.run(Unknown Source)

   Locked ownable synchronizers:
	- None

"thread applet-com.fsc.client.label.LabelEditorApplet.class-2" prio=4 tid=0x03342c00 nid=0x2cb8 in Object.wait() [0x042af000]
   java.lang.Thread.State: WAITING (on object monitor)
	at java.lang.Object.wait(Native Method)
	- waiting on <0x287cd808> (a javax.swing.text.html.HTMLDocument)
	at java.lang.Object.wait(Object.java:485)
	at javax.swing.text.AbstractDocument.readLock(Unknown Source)
	- locked <0x287cd808> (a javax.swing.text.html.HTMLDocument)
	at javax.swing.plaf.basic.BasicTextUI.getMinimumSize(Unknown Source)
	at javax.swing.JComponent.getMinimumSize(Unknown Source)
	at javax.swing.BoxLayout.checkRequests(Unknown Source)
	at javax.swing.BoxLayout.preferredLayoutSize(Unknown Source)
	- locked <0x2875c3a0> (a javax.swing.BoxLayout)
	at java.awt.Container.preferredSize(Unknown Source)
	- locked <0x28012738> (a java.awt.Component$AWTTreeLock)
	at java.awt.Container.getPreferredSize(Unknown Source)
	at javax.swing.JComponent.getPreferredSize(Unknown Source)
	at javax.swing.ViewportLayout.preferredLayoutSize(Unknown Source)
	at java.awt.Container.preferredSize(Unknown Source)
	- locked <0x28012738> (a java.awt.Component$AWTTreeLock)
	at java.awt.Container.getPreferredSize(Unknown Source)
	at javax.swing.JComponent.getPreferredSize(Unknown Source)
	at javax.swing.ScrollPaneLayout.preferredLayoutSize(Unknown Source)
	at java.awt.Container.preferredSize(Unknown Source)
	- locked <0x28012738> (a java.awt.Component$AWTTreeLock)
	at java.awt.Container.getPreferredSize(Unknown Source)
	at javax.swing.JComponent.getPreferredSize(Unknown Source)
	at java.awt.BorderLayout.preferredLayoutSize(Unknown Source)
	- locked <0x28012738> (a java.awt.Component$AWTTreeLock)
	at java.awt.Container.preferredSize(Unknown Source)
	- locked <0x28012738> (a java.awt.Component$AWTTreeLock)
	at java.awt.Container.getPreferredSize(Unknown Source)
	at javax.swing.JComponent.getPreferredSize(Unknown Source)
	at java.awt.BorderLayout.preferredLayoutSize(Unknown Source)
	- locked <0x28012738> (a java.awt.Component$AWTTreeLock)
	at java.awt.Container.preferredSize(Unknown Source)
	- locked <0x28012738> (a java.awt.Component$AWTTreeLock)
	at java.awt.Container.getPreferredSize(Unknown Source)
	at javax.swing.JComponent.getPreferredSize(Unknown Source)
	at javax.swing.JRootPane$RootLayout.preferredLayoutSize(Unknown Source)
	at java.awt.Container.preferredSize(Unknown Source)
	- locked <0x28012738> (a java.awt.Component$AWTTreeLock)
	at java.awt.Container.getPreferredSize(Unknown Source)
	at javax.swing.JComponent.getPreferredSize(Unknown Source)
	at java.awt.BorderLayout.preferredLayoutSize(Unknown Source)
	- locked <0x28012738> (a java.awt.Component$AWTTreeLock)
	at java.awt.Container.preferredSize(Unknown Source)
	- locked <0x28012738> (a java.awt.Component$AWTTreeLock)
	at java.awt.Container.getPreferredSize(Unknown Source)
	at java.awt.Window.pack(Unknown Source)
	at com.fsc.client.label.LabelEditorApplet.init(LabelEditorApplet.java:118)
	at com.sun.deploy.uitoolkit.impl.awt.AWTAppletAdapter.init(Unknown Source)
	at sun.plugin2.applet.Plugin2Manager$AppletExecutionRunnable.run(Unknown Source)
	at java.lang.Thread.run(Unknown Source)

   Locked ownable synchronizers:
	- None

 

After the analysis, it is clear that there are at least 2 threads waiting for locks hold by another thread:

  1. JEditorPane$PageLoader thread to load HTML page – it holds the HTMLDocument write-lock and waiting for AWTTreeLock<0x28012738> to enable a button.
  2. Applet main thread to invoke Window.pack() (which hold AWTTreeLock<0x28012738> and waiting for HTMLDocument read-lock to get the minimum size.

In Java Concurrency in Practice, it is a typical Lock-ordering Deadlocks.

What surprises me is that some innocent looking codes can actually cause deadlock – enable a button, Window.pack() etc.


The cause? I update my GUI from PageLoader thread.

The resolution? To update all GUI from the event dispatch thread through SwingUtilities.invokeLater().

(Based on my speculation, this method can prevent the deadlock because now, the PageLoader thread will no longer holding a lock while waiting for another lock.)

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: