Bug 136855 [IDE] Deadlock caused by IDEIdleHelper and HeapStatus calls to runFinalization from UI thread
Plugins that have multi-threaded access to a COM server dll whose ThreadingModel=Apartment are broken in Eclipse 3.2. COM implements cross-apartment COM calls using Windows messages. Thus the IUnknown::Release() calls from a thread in the multi-threaded-apartment (MTA) to an object owned by the single-threaded-apartment (STA) actually post a message to the main thread's dispatch loop and then block waiting for a response. The IUnknown::Release() calls typically happen during finalization of the Java objects that are accessing the COM server via JNI. The IDEIdleHelper and HeapStatus calls to System.runFinalization() from the UI thread cause it to block, thus its dispatch loop (i.e. Workbench.runEventLoop) is no longer functioning when objects are finalized. Result: deadlock. The System.runFinalization() method, unlike the System.gc() method, causes the calling thread to block on a Thread.join() call: Thread [main] (Suspended) Object.wait(long) line: not available [native method] Thread.join(long) line: 1038 Thread.join() line: 1091 Finalizer$1.run() line: 164 AccessController.doPrivileged1(PrivilegedAction) line: not available [native method] AccessController.doPrivileged(PrivilegedAction) line: 287 Finalizer.forkSecondaryFinalizer(Runnable) line: 170 Finalizer.runFinalization() line: 175 Runtime.runFinalization0() line: not available [native method] Runtime.runFinalization() line: 692 System.runFinalization() line: 833 IDEIdleHelper.performGC() line: 154 IDEIdleHelper$1.run() line: 125 Display.runTimer(int) line: 3409 Display.messageProc(int, int, int, int) line: 2691 OS.DispatchMessageW(MSG) line: not available [native method] OS.DispatchMessage(MSG) line: 1879 Display.readAndDispatch() line: 2964 Workbench.runEventLoop(Window$IExceptionHandler, Display) line: 1914 Workbench.runUI() line: 1878 Workbench.createAndRunWorkbench(Display, WorkbenchAdvisor) line: 419 PlatformUI.createAndRunWorkbench(Display, WorkbenchAdvisor) line: 143 IDEApplication.run(Object) line: 95 PlatformActivator$1.run(Object) line: 78 EclipseAppLauncher.runApplication(Object) line: 92 EclipseAppLauncher.start(Object) line: 68 EclipseStarter.run(Object) line: 376 EclipseStarter.run(String[], Runnable) line: 169 NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method] NativeMethodAccessorImpl.invoke(Object, Object[]) line: 85 NativeMethodAccessorImpl.invoke(Method, Object, Object[]) line: 58 DelegatingMethodAccessorImpl.invoke(Method, Object, Object[]) line: 60 Method.invoke(Object, Object[]) line: 391 Main.invokeFramework(String[], URL[]) line: 336 Main.basicRun(String[]) line: 280 Main.run(String[]) line: 977 Main.main(String[]) line: 952 Here's the relevent code from the Finalizer class: private static void forkSecondaryFinalizer(final Runnable proc) { PrivilegedAction pa = new PrivilegedAction() { public Object run() { ThreadGroup tg = Thread.currentThread().getThreadGroup(); for (ThreadGroup tgn = tg; tgn != null; tg = tgn, tgn = tg.getParent()); Thread sft = new Thread(tg, proc, "Secondary finalizer"); sft.start(); try { sft.join(); } catch (InterruptedException x) { /* Ignore */ } return null; }}; AccessController.doPrivileged(pa); } The "Secondary Finalizer" thread calls the Java objects' finalize() method, which calls IUnknown::Release(), which then deadlocks. Besides this deadlock behaviour, in principle, it's a bad idea to block the UI thread while doing finalizations. Especially since there's no compelling reason to do so. Either the calls to System.runFinalization() should be removed or they should be run from a background thread. This call is entirely optional anyways. The objects will eventually be finalized (though there may be compelling reasons to force the finalizations). Probably the best solution is to make both the call to gc() and to runFinalization() in a background thread. I have confirmed this behaviour using IBM VM versions 1.4.2 and Sun VM versions 1.4.2 and 1.5.0. I am unable to reproduce this using the IBM 1.5 vm. I have written a small Java-only testcase that demonstrates the deadlock. It emulates the scenario by doing a Display.syncExec() from a Java object's finalize() method so there's no need for the Eclipse team to actually use a Java/COM bridge to see the behaviour. I'll attach the testcase to the report shortly.
bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/IDEWorkbenchMessages.java
public static String ProjectCapabilitySelectionGroup_description;
	public static String ProjectCapabilitySelectionGroup_grayItems;
	public static String ProjectCapabilitySelectionGroup_disabledLabel;
	public static String ProjectCapabilitySelectionGroup_errorTitle;
	public static String ProjectCapabilitySelectionGroup_disabledCapability;
	public static String ProjectCapabilitySelectionGroup_invalidCapability;
	public static String ProjectCapabilitySelectionGroup_missingPrereqs;
	public static String ProjectCapabilitySelectionGroup_requiredPrereq;
	public static String ProjectCapabilitySelectionGroup_requiredPrereqs;
	public static String ProjectCapabilitySelectionGroup_membershipConflict;
	public static String ProjectCapabilitySelectionGroup_membershipPrereqConflict;
	public static String Capability_nameMissing;
	public static String ICategory_other;
	public static String WizardDataTransfer_existsQuestion;
	public static String WizardDataTransfer_overwriteNameAndPathQuestion;
	public static String WizardDataTransfer_exceptionMessage;
	public static String WizardTransferPage_selectTypes;
	public static String WizardTransferPage_selectAll;
	public static String WizardTransferPage_deselectAll;
	public static String NewProjectWizard_windowTitle;
	public static String NewProjectWizard_title;
	public static String NewProjectWizard_errorTitle;
	public static String NewProjectWizard_invalidCapabilities;
	public static String NewProjectWizard_noWizard;
	public static String CreateProjectWizard_errorTitle;
	public static String CreateProjectWizard_internalError;
	public static String CreateProjectWizard_caseVariantExistsError;
	public static String RemoveCapabilityWizard_errorMessage;
	public static String RemoveCapabilityWizard_internalError;
	public static String WizardNewProjectCapabilityPage_description;