This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

Bug 33271

Summary: Deadlock while switching workspaces.
Product: obsolete Reporter: ehucka <ehucka>
Component: simAssignee: issues@obsolete <issues>
Status: VERIFIED FIXED    
Severity: blocker CC: ethanrider, jchalupa, jglick, ttran
Priority: P2 Keywords: RANDOM, THREAD
Version: -S1S-   
Hardware: PC   
OS: Linux   
Issue Type: DEFECT Exception Reporter:
Attachments: IDE's thread dump.
proposed patch #2

Description ehucka 2003-04-28 11:37:04 UTC
Nevada build 030422.
Linux RH 7.2 JDK 1.4.1_02, GNome

Sometimes when I had switched from Editing
workspace to GUI Editing the IDE freezed. In GUI
Editing workspace I have opened several forms in
Forms editor.
Comment 1 ehucka 2003-04-28 11:37:52 UTC
Created attachment 10176 [details]
IDE's thread dump.
Comment 2 _ ttran 2003-04-28 12:05:17 UTC
it is logger module which calls Swing from outside AWT dispatch
thread.  See

"FolderChildren_Refresh" daemon prio=1 tid=0x0x884ba60 nid=0x4112
waiting for monitor entry [be9fe000..be9ff880]
	at java.awt.Container.invalidateTree(Container.java:1113)
	- waiting to lock <0x44a154f0> (a java.awt.Component$AWTTreeLock)
	at java.awt.Container.setFont(Container.java:1145)
	at javax.swing.JComponent.setFont(JComponent.java:2307)
	at javax.swing.LookAndFeel.installColorsAndFont(LookAndFeel.java:89)
	at
javax.swing.plaf.basic.BasicButtonUI.installDefaults(BasicButtonUI.java:124)
	at
javax.swing.plaf.metal.MetalButtonUI.installDefaults(MetalButtonUI.java:53)
	at javax.swing.plaf.basic.BasicButtonUI.installUI(BasicButtonUI.java:60)
	at javax.swing.JComponent.setUI(JComponent.java:449)
	at javax.swing.AbstractButton.setUI(AbstractButton.java:1594)
	at javax.swing.JButton.updateUI(JButton.java:119)
	at javax.swing.AbstractButton.init(AbstractButton.java:1930)
	at javax.swing.JButton.<init>(JButton.java:109)
	at javax.swing.JButton.<init>(JButton.java:82)
	at org.netbeans.core.LoggerButton.initialize(LoggerButton.java:76)
	at org.netbeans.core.LoggerButton.addLogButton(LoggerButton.java:89)
	at
org.netbeans.modules.sim.bugsubmitter.ui.BugsubmitterErrorManager.notify(BugsubmitterErrorManager.java:204)
	- locked <0x44c5dec8> (a
org.netbeans.modules.sim.bugsubmitter.ui.BugsubmitterErrorManager)
	at
org.openide.ErrorManager$DelegatingErrorManager.notify(ErrorManager.java:390)
	at org.openide.loaders.InstanceNode.initName(InstanceNode.java:259)
	at org.openide.loaders.InstanceNode.<init>(InstanceNode.java:78)
	at org.openide.loaders.InstanceNode.<init>(InstanceNode.java:64)
	at
org.openide.loaders.InstanceDataObject.createNodeDelegateImpl(InstanceDataObject.java:463)
	at
org.openide.loaders.InstanceDataObject.createNodeDelegate(InstanceDataObject.java:412)
	at org.openide.loaders.DataObject$1.run(DataObject.java:242)
	- locked <0x442e3a88> (a java.lang.Object)
	at org.openide.util.Mutex.readAccess(Mutex.java:239)
	at org.openide.loaders.DataObject.getNodeDelegate(DataObject.java:238)
	at
org.openide.loaders.DataObject.getClonedNodeDelegate(DataObject.java:263)
	at org.openide.loaders.FolderChildren.createNodes(FolderChildren.java:132)
	at org.openide.nodes.Children$Keys$KE.nodes(Children.java:1983)
	at org.openide.nodes.ChildrenArray.nodesFor(ChildrenArray.java:109)
	at org.openide.nodes.Children$Info.nodes(Children.java:1077)
	at org.openide.nodes.Children.updateAdd(Children.java:885)
	at org.openide.nodes.Children.setEntries(Children.java:672)
	at org.openide.nodes.Children$3.run(Children.java:1874)
	at org.openide.util.Mutex.postRequest(Mutex.java:875)
	at org.openide.util.Mutex.postWriteRequest(Mutex.java:375)
	at org.openide.nodes.Children$Keys.applyKeys(Children.java:1882)
	at org.openide.nodes.Children$Keys.setKeys(Children.java:1841)
	at org.openide.loaders.FolderChildren.access$501(FolderChildren.java:32)
	at
org.openide.loaders.FolderChildren$ChildrenRefreshRunnable.run(FolderChildren.java:250)
	at org.openide.util.Task.run(Task.java:136)
	at org.openide.util.RequestProcessor$Task.run(RequestProcessor.java:328)
	at
org.openide.util.RequestProcessor$Processor.run(RequestProcessor.java:670)

Comment 3 Jan Chalupa 2003-04-29 09:47:25 UTC
I guess you meant the 'sim' module, not 'logger'. Reassigning.
Comment 4 Ethan Rider 2003-04-29 22:39:54 UTC
I have not been able to actually cause this deadlock in several
attempts (setting an internal executor and calling notify with
ErrorLevel of Informational from a new thread hasn't caused it).  I
was hoping that the reporter (or some other party) could assist in
providing a test case or showing a way to reliably reproduce this issue.

An alternate (less desirable) solution is to provide an nbm with the
proposed fix should the issue prove reliably reproducible in only one
environment.


Proposed fix:
As far as this thread dump can indicate, it seems that the
construction of the LoggerButton in the LoggerButton.initialize()
method is causing this locking issue, so pushing the construction of
the Button to the AWT Event Thread using an invokeLater runnable is
the proposed solution:

Diff:
cvs diff -uw LoggerButton.java
Index: LoggerButton.java
===================================================================
RCS file: /cvs/sim/bugsubmitter/src/org/netbeans/core/LoggerButton.java,v
retrieving revision 1.6.2.1
diff -u -w -r1.6.2.1 LoggerButton.java
--- LoggerButton.java   10 Mar 2003 17:33:32 -0000      1.6.2.1
+++ LoggerButton.java   29 Apr 2003 21:35:24 -0000
@@ -71,11 +71,15 @@
         if (initialized) 
                return;
 
-        ResourceBundle b;
-       b =
ResourceBundle.getBundle("org/netbeans/modules/sim/bugsubmitter/ui/Bundle");
// NOI18N
+        final ResourceBundle b =
ResourceBundle.getBundle("org/netbeans/modules/sim/bugsubmitter/ui/Bundle");
// NOI18N
+        javax.swing.SwingUtilities.invokeLater(new Runnable() {
+            public void run() {
         log = new JButton(b.getString("LBL_LOG_REPORT")); // NOI18N
        
log.setMnemonic(b.getString("LBL_LOG_REPORT_MNEMONIC").charAt(0)); //
NOI18N
         log.addActionListener(new LoggerButton());
+       }
+});
+
 
        Class tmp = BugsubmitterWizardAction.class;
        bswa = (BugsubmitterWizardAction)
BugsubmitterWizardAction.get(tmp);

Comment 5 Terry Heatlie 2003-04-29 23:43:16 UTC
I'm probably just showing my ignorance here, but I though
it was okay to construct Swing objects outside the event
thread.  I thought the only problem was accessing them after
they were realized (in the X11 sense of that word).  The SIM
code in question only constructs a button, it doesn't realize
it until it is on the AWT thread.  Never the less, it should
be harmless to run that code in an invokeLater(), so we probably
should go ahead and do that.

Also, the AWT thread seems to be hung up trying to get a 
read lock on the children of some nodes in the 
component palette (um, I think...)  At first sight, that
doesn't seem to be anything to do with the kind of Swing
lossage one would expect from a bug which accessed Swing
from the wrong thread.  But of course, there may be some
connection I don't see.  I'd welcome further comments on
that...

Comment 6 Ethan Rider 2003-04-30 01:09:58 UTC
I agree with Terry's statement that the construction of a 
GUI object without displaying that object seems to be the 
sort of thing one *should* be able to do without being on 
the event thread, and it does seem innoculous to create 
the JButton in invokeLater...

I'd like to define more clearly the test case that causes 
this issue or have some notion about how it is reproduced, 
otherwise the proposed fix cannot be confirmed as a 
resolution to this issue.

Has anyone been able to reliably cause this to occur?
Comment 7 Jan Chalupa 2003-04-30 12:49:28 UTC
Could any of core developers with expertise in AWT/Swing respond to
Terry's and Ethan's comments? Creating a not yet displayed button from
a non-AWT thread should be perfectly safe, shouldn't it? Is this the
real cause of the problem?

To Eman: are you able to reproduce reliably/often? Can you provide a
more specific test case?

I don't think Ethan's fix should go into release35 until there is a
clear understanding of the problem.
Comment 8 ehucka 2003-04-30 14:04:31 UTC
I reproduced it only twice. In both cases I only started IDE into
Editing workspace with several forms opened in GUI Editing workspace.
Then I switched to GUI Editing workspace and the deadlock occured.
Comment 9 David Simonek 2003-04-30 17:43:45 UTC
"Creating a not yet displayed button from a non-AWT thread should be
perfectly safe, shouldn't it?"
I thought that too, but unfortunately it's not true, especially in
Netbeans context.
Correct sentence is "Creating and manipulating of not yet realized
swing controls from a non-AWT thread is legal." Nothing about that
it's safe, unfortunately.

Problem is that swing control constructor code often locks
java.awt.Component$AWTTreeLock. I thought that it is kind of violation
of Swing thread model, and I filled in bugtraq bug #4833969.
But JDK engineers say that such code is valid.

Result: You *can* manipulate not-yet realized swing controls in
non-AWT thread, but you have to be sure (to be safe) that:
1) AWT thread will not wait for completion of the thread where swing
controls are being constructed.
2) You don't manipulate swing controls in non-AWT thread under some
public lock that code in AWT thread can lock as well.

When you translate above to the Netbens context, result is: In currect
Netbeans threading model, you can't safely build and construct swing
components in request processor threads.

So above proposed patch is AFAIK only easy way how to correct this
problem now.

CCing thread guru Jesse in case I'm wrong.
Comment 10 Jesse Glick 2003-04-30 18:11:32 UTC
Basically agree with David's summary. Small refinement: it should be
safe to build an unrealized Swing component in a request processor
thread if you posted the top runnable to it, because then you know
there are no held locks which AWT code might try to grab.
Comment 11 Ethan Rider 2003-04-30 23:54:44 UTC
Because of the way the LoggerButton code is meant to work, and the
multiple uses of initialize, the proposed fix needs to be modified
such that:
0) No changes are made to initialize()

1) In ensureButtonRegistered() we check to see if we are AWT EDT and
if not use an invokeAndWait to actually ensure we register the button
before returning.

2) Move the call to initialize() inside the invokeLater() runnable
already in the addLoggerButton method to ensure initialization of the
log button prior to use.

If this seems acceptable it will be committed on 4/31
Comment 12 Jesse Glick 2003-05-01 00:21:57 UTC
Just be careful with invokeAndWait - you need to ensure that you are
not holding any locks when you call it, or you can produce a different
deadlock!
Comment 13 Terry Heatlie 2003-05-01 03:39:42 UTC
The case where we would do the invokeAndWait() is from the
actionPerformed() of a CallableSystemAction subclass.
It looks like it is running on a RequestProcessor thread
(Module-Actions).  I looked at openide/doc-files/threading.html
where it says "Actions which definitely require the event thread
should use it".  Does this imply that it is okay to call
invokeAndWait() in these circumstances? I'm not entirely
clear what that sentence is saying (probably my fault, sorry...)

Comment 14 Jesse Glick 2003-05-01 15:27:24 UTC
Yes, actions invoked by the ActionManager are run in a private request
processor with no external locks held, so that is OK.
Comment 15 Ethan Rider 2003-05-01 15:39:52 UTC
Created attachment 10204 [details]
proposed patch #2
Comment 16 Ethan Rider 2003-05-01 23:11:23 UTC
Proposed fix #2 committed to nevada branch and trunk.
Comment 17 Jan Chalupa 2003-05-02 08:52:19 UTC
TM -> 'S1S 5'.
Comment 18 ehucka 2003-08-18 12:06:35 UTC
Verified.