Project: Eclipse_Platform_UI
Bug 136855 [IDE] Deadlock caused by IDEIdleHelper and HeapStatus calls to runFinalization from UI thread
Reported: 2006-04-14 11:58:06 | Status: verified fixed

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.

Files changed in fix:
bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/IDEIdleHelper.java bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/IDEWorkbenchMessages.java bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/HeapStatus.java