Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
Bug 321155 - [DynamicGUI] UIExtensionTracker calling Display.syncExec() on disposed Display
Summary: [DynamicGUI] UIExtensionTracker calling Display.syncExec() on disposed Display
Status: VERIFIED FIXED
Alias: None
Product: Platform
Classification: Eclipse Project
Component: UI (show other bugs)
Version: 3.6   Edit
Hardware: All All
: P3 normal with 1 vote (vote)
Target Milestone: 3.6.2   Edit
Assignee: Eric Moffatt CLA
QA Contact: Eric Moffatt CLA
URL:
Whiteboard:
Keywords:
Depends on:
Blocks: 398909
  Show dependency tree
 
Reported: 2010-07-28 13:17 EDT by Tom Houser CLA
Modified: 2013-01-23 12:14 EST (History)
2 users (show)

See Also:
pwebster: review+
bokowski: review+


Attachments
Check if the Display is disposed (945 bytes, patch)
2010-10-13 12:12 EDT, Eric Moffatt CLA
no flags Details | Diff
Patch that checks for 'isDisposed' in both methods (1.20 KB, patch)
2010-10-14 11:10 EDT, Eric Moffatt CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Tom Houser CLA 2010-07-28 13:17:41 EDT
Build Identifier: Eclipse SDK 3.6GA I20100608-0911

We have an RCP application that dynamically loads plugins with extensions.  The RCP application also dynamically unloads these plugins right before shutting down.

We get these exceptions in the <workspace>/.metadata/.log during shutdown:

!ENTRY org.eclipse.equinox.registry 4 2 2010-07-28 10:07:45.309
!MESSAGE Problems occurred when invoking code from plug-in: "org.eclipse.equinox.registry".
!STACK 0
org.eclipse.swt.SWTException: Device is disposed
	at org.eclipse.swt.SWT.error(SWT.java:4083)
	at org.eclipse.swt.SWT.error(SWT.java:3998)
	at org.eclipse.swt.SWT.error(SWT.java:3969)
	at org.eclipse.swt.widgets.Display.error(Display.java:1204)
	at org.eclipse.swt.widgets.Display.syncExec(Display.java:4285)
	at org.eclipse.ui.internal.registry.UIExtensionTracker.applyRemove(UIExtensionTracker.java:42)
	at org.eclipse.core.runtime.dynamichelpers.ExtensionTracker.notify(ExtensionTracker.java:153)
	at org.eclipse.core.runtime.dynamichelpers.ExtensionTracker.doRemove(ExtensionTracker.java:179)
	at org.eclipse.core.runtime.dynamichelpers.ExtensionTracker.registryChanged(ExtensionTracker.java:119)
	at org.eclipse.core.internal.registry.ExtensionRegistry$2.run(ExtensionRegistry.java:921)
	at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42)
	at org.eclipse.core.internal.registry.ExtensionRegistry.processChangeEvent(ExtensionRegistry.java:919)
	at org.eclipse.core.runtime.spi.RegistryStrategy.processChangeEvent(RegistryStrategy.java:260)
	at org.eclipse.core.internal.registry.osgi.ExtensionEventDispatcherJob.run(ExtensionEventDispatcherJob.java:50)
	at org.eclipse.core.internal.jobs.Worker.run(Worker.java:54)

!ENTRY org.eclipse.equinox.registry 4 0 2010-07-28 10:07:45.320
!MESSAGE Error notifying registry change listener.
!SUBENTRY 1 org.eclipse.equinox.registry 4 0 2010-07-28 10:07:45.320
!MESSAGE Error notifying registry change listener.
!STACK 0
org.eclipse.swt.SWTException: Device is disposed
	at org.eclipse.swt.SWT.error(SWT.java:4083)
	at org.eclipse.swt.SWT.error(SWT.java:3998)
	at org.eclipse.swt.SWT.error(SWT.java:3969)
	at org.eclipse.swt.widgets.Display.error(Display.java:1204)
	at org.eclipse.swt.widgets.Display.syncExec(Display.java:4285)
	at org.eclipse.ui.internal.registry.UIExtensionTracker.applyRemove(UIExtensionTracker.java:42)
	at org.eclipse.core.runtime.dynamichelpers.ExtensionTracker.notify(ExtensionTracker.java:153)
	at org.eclipse.core.runtime.dynamichelpers.ExtensionTracker.doRemove(ExtensionTracker.java:179)
	at org.eclipse.core.runtime.dynamichelpers.ExtensionTracker.registryChanged(ExtensionTracker.java:119)
	at org.eclipse.core.internal.registry.ExtensionRegistry$2.run(ExtensionRegistry.java:921)
	at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42)
	at org.eclipse.core.internal.registry.ExtensionRegistry.processChangeEvent(ExtensionRegistry.java:919)
	at org.eclipse.core.runtime.spi.RegistryStrategy.processChangeEvent(RegistryStrategy.java:260)
	at org.eclipse.core.internal.registry.osgi.ExtensionEventDispatcherJob.run(ExtensionEventDispatcherJob.java:50)
	at org.eclipse.core.internal.jobs.Worker.run(Worker.java:54)

This happens because there is a race condition between the main UI thread and the ExtensionEventDispatcherJob worker thread during shutdown such that registry change events are being handled after the display has been disposed during shutdown.  The registry change events that are fired are due to the dynamic unloading of plugins with extensions.

Either UIExtensionTracker should be written to handle the case when the display is disposed (i.e. handle the exception and quietly ignore it) or the extension registry should provide an API that the UI can call during shutdown that waits for these ExtensionEventDispatcherJob jobs to complete (e.g. calls IJobManager.join() on a job family for these jobs).

Reproducible: Always
Comment 1 Eric Moffatt CLA 2010-10-13 12:12:02 EDT
Created attachment 180786 [details]
Check if the Display is disposed
Comment 2 Eric Moffatt CLA 2010-10-13 12:13:19 EDT
Paul / Boris, could you please take a look at this (very simple) patch and vote ?
Comment 3 Paul Webster CLA 2010-10-13 12:22:22 EDT
Tom, where are you in the shutdown cycle that you are removing extensions?

PW
Comment 4 Paul Webster CLA 2010-10-13 12:23:37 EDT
(In reply to comment #1)
> Created an attachment (id=180786) [details]
> Check if the Display is disposed

Eric, it looks good.  I'd add the same thing to the applyAdd(*) method as well.

PW
Comment 5 Tom Houser CLA 2010-10-13 12:54:32 EDT
Re: comment 3

Paul, as I stated in the description.  The app dynamically unloads plugins with extensions (that were dynamically loaded earlier in the app's execution)immediately before the shutdown cycle.  Since the extension registry uses jobs (i.e. ExtensionEventDispatcherJob) to fire registry events (i.e. the "remove" events caused by the dynamic unloading of plugins with extensions), there is a race condition between the worker thread that is running the ExtensionEventDispatcherJob and the main UI thread which is subsequently shutdown.

The patch looks good to me (and I agree with Paul that the same change should be made to applyAdd()).
Comment 6 Paul Webster CLA 2010-10-13 13:08:36 EDT
(In reply to comment #5)
> Re: comment 3
> 
> Paul, as I stated in the description.  The app dynamically unloads plugins with
> extensions (that were dynamically loaded earlier in the app's

I'm asking about the trigger:  WorkbenchAdvisor#preShutdown()?  What event causes the dynamic unloading to begin?  Is it related to the workbench shutdown cycle at all, or an external stimulus?

PW
Comment 7 Boris Bokowski CLA 2010-10-13 13:57:14 EDT
Looks good to me (and yes, please add the same check to the add method as well)
Comment 8 Tom Houser CLA 2010-10-13 14:14:42 EDT
Re: comment 6

The trigger is not related to the workbench shutdown cycle.  The trigger occurs before the workbench is shutdown.  The RCP application programmatically makes the necessary calls to unload the bundles that it had dynamically loaded earlier in its execution and then explicitly calls PlatformUI.getWorkbench().close() to shutdown the app.

To dynamically install plugins, we use org.osgi.framework.BundleContext.installBundle(String) followed by org.osgi.service.packageadmin.PackageAdmin.refreshPackages(Bundle[]).

To dynamically uninstall plugins, we use org.osgi.framework.Bundle.stop(), followed by org.osgi.framework.Bundle.uninstall(), followed by org.osgi.service.packageadmin.PackageAdmin.refreshPackages(Bundle[]).

The RCP app is presenting a wizard dialog.  On the wizard pages, there is a Cancel button.  The user clicks on Cancel.  This triggers code that unloads the bundles and then calls PlatformUI.getWorkbench().close().
Comment 9 Paul Webster CLA 2010-10-13 16:28:33 EDT
(In reply to comment #8)
> The trigger is not related to the workbench shutdown cycle.  The trigger occurs
> before the workbench is shutdown.  The RCP application programmatically makes
> the necessary calls to unload the bundles that it had dynamically loaded
> earlier in its execution and then explicitly calls
> PlatformUI.getWorkbench().close() to shutdown the app.

Thanx Tom,

That seems reasonable behaviour.

PW
Comment 10 Eric Moffatt CLA 2010-10-14 11:10:30 EDT
Created attachment 180887 [details]
Patch that checks for 'isDisposed' in both methods
Comment 11 Eric Moffatt CLA 2010-10-14 11:11:47 EDT
Committed in >20101014. Applied the patch to the maintenance branch.
Comment 12 Paul Webster CLA 2011-01-20 12:24:09 EST
In M20110119-0834
PW