NEW DATE! Bugzilla will undergo maintenance 2024-03-28 18h00 CET. Bugzilla will be placed in read-only mode at that time.

Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
Bug 12827 - Deadlock calling createExecutableExtension during startup
Summary: Deadlock calling createExecutableExtension during startup
Status: RESOLVED FIXED
Alias: None
Product: Platform
Classification: Eclipse Project
Component: Runtime (show other bugs)
Version: 2.0   Edit
Hardware: PC Windows 2000
: P2 major (vote)
Target Milestone: 3.0   Edit
Assignee: platform-runtime-inbox CLA
QA Contact:
URL:
Whiteboard:
Keywords:
: 46438 (view as bug list)
Depends on:
Blocks:
 
Reported: 2002-04-04 15:14 EST by Elson Yuen CLA
Modified: 2005-01-05 14:03 EST (History)
4 users (show)

See Also:


Attachments
code.txt (2.13 KB, text/plain)
2002-04-04 15:28 EST, DJ Houghton CLA
no flags Details
Dump stack when eclipse startup hangs (13.60 KB, text/plain)
2002-04-04 19:04 EST, Elson Yuen CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Elson Yuen CLA 2002-04-04 15:14:09 EST
During the plugin initialization of the plugin 
com.ibm.etools.websphere.tools.v4, a resource change listener has been added to 
the workspace.  When the workbench restarts, the 
IResourceChangeListener.resourceChanged() is called.  This causes the 
IResourceChangeEvent.getDelta() to be called but this method is never returned.  
This problem causes the workbench hangs there and fail to startup.

I have added some system out print statements to the resource change listener:
======================================================================
...
		public void resourceChanged(IResourceChangeEvent event) 
		{			
System.out.println("Resource changed: " + event);			
// Print the stack trace.
try {
	String hello = null;
	hello.toString();
} catch (Exception e) {
	e.printStackTrace();
}
			IResourceDelta delta = event.getDelta();
System.out.println("Resource delta is: " + delta);			
			if (delta == null)
				return;

			// search for changes to remote file transfer instance 
using a visitor
			try {
...
======================================================================

Here is the system out that I get when I restart the eclipse workbench:
=====================================================================
Resource changed: 
org.eclipse.core.internal.events.ResourceChangeEvent[source=org.eclipse.core.int
ernal.resources.Workspace@41570edc]
java.lang.NullPointerException
        at 
com.ibm.etools.websphere.tools.EARConfigChangeManager$EARConfigChangeListener.re
sourceChanged(EARConfigChangeManager.java:77)
        at 
org.eclipse.core.internal.events.NotificationManager$1.run(NotificationManager.j
ava:123)
        at 
org.eclipse.core.internal.runtime.InternalPlatform.run(InternalPlatform.java:817
)
        at org.eclipse.core.runtime.Platform.run(Platform.java:395)
        at 
org.eclipse.core.internal.events.NotificationManager.notify(NotificationManager.
java:138)
        at 
org.eclipse.core.internal.events.NotificationManager.broadcastChanges(Notificati
onManager.java:62)
        at 
org.eclipse.core.internal.resources.Workspace.broadcastChanges(Workspace.java:13
4)
        at 
org.eclipse.core.internal.resources.Workspace.endOperation(Workspace.java:748)
        at 
org.eclipse.core.internal.resources.Workspace.run(Workspace.java:1343)
        at 
org.eclipse.debug.internal.core.BreakpointManager$3.run(BreakpointManager.java:5
64)
        at java.lang.Thread.run(Thread.java:498)
=====================================================================

From the system out, you can see that the "Resource changed" line is printed out 
and the stack trace is available.  The "Resource delta is: ..." line is not 
displayed means that the IResourceChangeEvent.getDelta( ) method never gets 
returned.  It simply hangs in the event.getDelta( ) method and the CPU usage is 
0%.  Therefore, it does not looks like it goes to an infinite loop of some kind.

Here is the whole resource change listener class EARConfigChangeListener for 
reference: 
=================================================================
	/**
	 * EARConfigChangeListener -- tracks the changes in the application.xml
	 * in an EAR project.
	 */
	public class EARConfigChangeListener implements IResourceChangeListener 
	{
		/**
		 * Create a new EARConfigChangeListener.
		 */
		public EARConfigChangeListener() 
		{
			super();
		}
		/**
		 * Listen for projects being added or removed and act 
accordingly.
		 *
		 * @param event org.eclipse.core.resources.IResourceChangeEvent
		 */
		public void resourceChanged(IResourceChangeEvent event) 
		{			
System.out.println("Resource changed: " + event);			
// Print the stack trace.
try {
	String hello = null;
	hello.toString();
} catch (Exception e) {
	e.printStackTrace();
}
			IResourceDelta delta = event.getDelta();
System.out.println("Resource delta is: " + delta);			
			if (delta == null)
				return;

			// search for changes to remote file transfer instance 
using a visitor
			try {
				delta.accept(new IResourceDeltaVisitor() {
					public boolean visit(IResourceDelta 
delta2) {
						IResource resource = 
delta2.getResource();
						if (resource == null)
							return true;

						if 
(EAR_APPLICATION_XML_FILENAME.equals(resource.getName())) {
							
earConfigChanged(resource.getProject());
						} else if (resource instanceof 
IProject) {
							projectChanged(delta2);
							return false;
						}
						return true;
					}
				});
			} catch (Exception e) {}			
		}
		/**
		 * React to a change within a project.
		 *
		 * @param delta org.eclipse.core.resources.IResourceDelta
		 */
		protected void projectChanged(IResourceDelta delta) 
		{
			IResourceDelta[] children = delta.getAffectedChildren();

			int size = children.length;
			for (int i = 0; i < size; i++) {
//				IResourceDelta child = children[i];

				// look for server instances and configurations
				try {
					delta.accept(new IResourceDeltaVisitor() 
{
						public boolean 
visit(IResourceDelta delta2) {
							IResource resource = 
delta2.getResource();
//							String resourceName = 
resource.getName();
							if 
(EAR_APPLICATION_XML_FILENAME.equals(resource.getName())) {
								
earConfigChanged(resource.getProject());
								return false;
							} else
								return true;
						}
					});
				} catch (Exception e) {}
			}
		}
	}	
===========================================================
Comment 1 DJ Houghton CLA 2002-04-04 15:28:31 EST
Created attachment 546 [details]
code.txt
Comment 2 DJ Houghton CLA 2002-04-04 15:28:50 EST
Created attachment with code for improved readability.
Comment 3 DJ Houghton CLA 2002-04-04 15:35:47 EST
Which build are you using?
Comment 4 Elson Yuen CLA 2002-04-04 15:42:54 EST
The driver is based on the M4 driver (based on 0321 stable build)
Comment 5 John Arthorne CLA 2002-04-04 16:09:38 EST
Your listener code contains an infinite loop.  In projectChanged, you 
recursively create another visitor on the same project delta.  I think you meant 
to create a vistor on the child delta here.

Note: IResourceChangeEvent.getDelta() is just an accessor method, it doesn't do 
any work.
Comment 6 Elson Yuen CLA 2002-04-04 19:04:34 EST
Created attachment 547 [details]
Dump stack when eclipse startup hangs
Comment 7 Elson Yuen CLA 2002-04-04 19:10:36 EST
I have changed the projectChanged() method to prevent the infinite loop. The 
problem still exists.  That part of the code never gets executed.  I did a dump 
stack and attached it with the defect.

It looks like the plugin class loaders have dead locked.  If you take a look at 
the dump stack, you'll notice that the main thread and the debug/breakpoint 
manager thread both have a lock on a different plugin class loader and are 
locking when trying to obtain a lock on the other's class loader.

Plugins should not have to worry about the class loader's behaviour or 
possible deadlocks created by threads started by the workbench, so I am 
reopening this defect.
Comment 8 John Wiegand CLA 2002-04-05 10:51:55 EST
Any attempt to createExecutableExtension during plugin startup will likely
cause deadlock in the current implementation.
Only simple bookkeeping operations can be done safely.
The ServerPlugin is doing much more than this.
There is an attempt to improve the robustness of the startup code evaluation,
but it is a wise design move to eliminate complex processing from startup.
Comment 9 John Arthorne CLA 2002-04-05 11:51:30 EST
Changed title to reflect real problem.  See bug 5875 for a more complete 
discussion of this issue.  See also "Note 3" in the javadoc for 
Plugin.startup.  The culprit seems to be:

com.ibm.etools.server.core.internal.ServerFactory.getDelegate

called from:

com.ibm.etools.server.core.internal.plugin.ServerPlugin.startup

We realize this is a limitation of the platform, and we'd like to solve it in 
the future if we can.  In the short term, it would be best to follow JohnW's 
advice and avoid heavy initialization code in Plugin.startup() methods.
Comment 10 Tim deBoer CLA 2002-04-08 21:51:32 EDT
John(s):

1) If this is a temporary limitation of the platform, can you give us a quick 
idea of the scope of the problem/fix and whether it is likely to be fixed in 
this release?

2) Is calling createExecutableExtension() the only case that might cause 
deadlock? Are there any other method calls that might also hang the workbench 
during startup? Since the penalty for leaving in one of these calls is severe, 
we'd like to know exactly what circumstances could trigger the problem.

3) This restriction is not public knowledge, and we know of other plugins that 
may also load extension points on startup. In a large product with many cross 
dependancies, it's hard to completely ensure that none of these methods are 
being called during plugin startup. (a "simple" method call to a required 
plugin may turn around and load other plugins via another method call)

In light of when a fix might be available and not doing a lot of temporary 
work, is it reasonable for you to detect these situations and explicitly 
disallow them in current builds? (For instance, can you throw an exception when 
createExecutableExtension() is called during plugin startup?) This would 
cleanly enforce this restriction and ensure that other plugins do not run into 
the same problem.
Comment 11 John Arthorne CLA 2002-04-09 11:04:21 EDT
1) I don't think it is likely this will be addressed in the 2.0 timeframe.  This 
is mainly a function of the risk involved with significantly modifying the 
classloading mechanism.  The fix, if feasible, would be localized within the 
classloader code, so it would be transparent to all users.  

2) No. Any two orthogonal locking mechanisms can combine to cause deadlock.  The 
classloader introduces one lock, so if you try to acquire any other lock within 
the startup() code, deadlock will be possible.  Quite simply, if the startup() 
blocks while waiting on a lock, and the thread that holds that lock attempts a 
classload in the same plugin, you'll get deadlock.

We will consider your suggestion for disallowing createExecutableExtension 
during startup().  I don't know how difficult this would be to implement.  
Unfortunately this is only one instance of a general problem, so it won't be a 
great help.  
Comment 12 John Arthorne CLA 2003-11-12 11:16:40 EST
*** Bug 46438 has been marked as a duplicate of this bug. ***
Comment 13 John Arthorne CLA 2004-02-10 14:37:16 EST
If we don't fix for 3.0, we should minimally document the behavior (for example 
in Plugin.startup javadoc).
Comment 14 Pascal Rapicault CLA 2005-01-05 14:03:45 EST
These deadlock cases have been adressed in the osgi based runtime.