Bug 563768 - Don't attempt to save preferences at shutdown
Summary: Don't attempt to save preferences at shutdown
Status: NEW
Alias: None
Product: Platform
Classification: Eclipse Project
Component: Resources (show other bugs)
Version: 4.16   Edit
Hardware: All All
: P3 normal (vote)
Target Milestone: ---   Edit
Assignee: Platform-Resources-Inbox CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2020-05-30 20:42 EDT by Alex Blewitt CLA
Modified: 2020-06-10 06:58 EDT (History)
1 user (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Alex Blewitt CLA 2020-05-30 20:42:55 EDT
When shutting down bundles, a call is made from the ResourcesPlugin to save preferences. However, the documentation for the (deprecated) savePreferences method indicates that these preferences are not, in fact, saved on plugin shutdown.

If the platform is sufficiently optimised to allow plugins to be shut down in arbitrarty orders, then it's possible for the resources plugin to be shut down prior to other bundles. This results in the save method attempting to get the internal platform and then throwing an Assertion failure because it has been shut down at this point.

Not picking on JDT (it's just one of the many in the log) but the error messages look like this:

--- 8< ---
!ENTRY org.eclipse.jdt.debug.ui 4 0 2020-05-31 01:23:19.398
!MESSAGE FrameworkEvent ERROR
!STACK 0
org.osgi.framework.BundleException: Exception in org.eclipse.jdt.internal.debug.ui.JDIDebugUIPlugin.stop() of bundle org.eclipse.jdt.debug.ui.
	at org.eclipse.osgi.internal.framework.BundleContextImpl.stop(BundleContextImpl.java:919)
	at org.eclipse.osgi.internal.framework.EquinoxBundle.stopWorker0(EquinoxBundle.java:1029)
	at org.eclipse.osgi.internal.framework.EquinoxBundle$EquinoxModule.stopWorker(EquinoxBundle.java:370)
	at org.eclipse.osgi.container.Module.doStop(Module.java:658)
	at org.eclipse.osgi.container.Module.stop(Module.java:520)
	at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.decStartLevel(ModuleContainer.java:1885)
	at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.doContainerStartLevel(ModuleContainer.java:1760)
	at org.eclipse.osgi.container.SystemModule.stopWorker(SystemModule.java:275)
	at org.eclipse.osgi.internal.framework.EquinoxBundle$SystemBundle$EquinoxSystemModule.stopWorker(EquinoxBundle.java:202)
	at org.eclipse.osgi.container.Module.doStop(Module.java:658)
	at org.eclipse.osgi.container.Module.stop(Module.java:520)
	at org.eclipse.osgi.container.SystemModule.stop(SystemModule.java:207)
	at org.eclipse.osgi.internal.framework.EquinoxBundle$SystemBundle$EquinoxSystemModule$1.run(EquinoxBundle.java:220)
	at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: org.eclipse.core.runtime.AssertionFailedException: assertion failed: The application has not been initialized.
	at org.eclipse.core.runtime.Assert.isTrue(Assert.java:113)
	at org.eclipse.core.internal.runtime.InternalPlatform.assertInitialized(InternalPlatform.java:139)
	at org.eclipse.core.internal.runtime.InternalPlatform.getInstanceLocation(InternalPlatform.java:331)
	at org.eclipse.core.runtime.Plugin.savePluginPreferences(Plugin.java:341)
	at org.eclipse.ui.plugin.AbstractUIPlugin.savePreferenceStore(AbstractUIPlugin.java:579)
	at org.eclipse.ui.plugin.AbstractUIPlugin.stop(AbstractUIPlugin.java:682)
	at org.eclipse.jdt.internal.debug.ui.JDIDebugUIPlugin.stop(JDIDebugUIPlugin.java:462)
	at org.eclipse.osgi.internal.framework.BundleContextImpl$4.run(BundleContextImpl.java:899)
	at org.eclipse.osgi.internal.framework.BundleContextImpl$4.run(BundleContextImpl.java:1)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at org.eclipse.osgi.internal.framework.BundleContextImpl.stop(BundleContextImpl.java:891)
	... 13 more
Root exception:
org.eclipse.core.runtime.AssertionFailedException: assertion failed: The application has not been initialized.
	at org.eclipse.core.runtime.Assert.isTrue(Assert.java:113)
	at org.eclipse.core.internal.runtime.InternalPlatform.assertInitialized(InternalPlatform.java:139)
	at org.eclipse.core.internal.runtime.InternalPlatform.getInstanceLocation(InternalPlatform.java:331)
	at org.eclipse.core.runtime.Plugin.savePluginPreferences(Plugin.java:341)
	at org.eclipse.ui.plugin.AbstractUIPlugin.savePreferenceStore(AbstractUIPlugin.java:579)
	at org.eclipse.ui.plugin.AbstractUIPlugin.stop(AbstractUIPlugin.java:682)
	at org.eclipse.jdt.internal.debug.ui.JDIDebugUIPlugin.stop(JDIDebugUIPlugin.java:462)
	at org.eclipse.osgi.internal.framework.BundleContextImpl$4.run(BundleContextImpl.java:899)
	at org.eclipse.osgi.internal.framework.BundleContextImpl$4.run(BundleContextImpl.java:1)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at org.eclipse.osgi.internal.framework.BundleContextImpl.stop(BundleContextImpl.java:891)
	at org.eclipse.osgi.internal.framework.EquinoxBundle.stopWorker0(EquinoxBundle.java:1029)
	at org.eclipse.osgi.internal.framework.EquinoxBundle$EquinoxModule.stopWorker(EquinoxBundle.java:370)
	at org.eclipse.osgi.container.Module.doStop(Module.java:658)
	at org.eclipse.osgi.container.Module.stop(Module.java:520)
	at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.decStartLevel(ModuleContainer.java:1885)
	at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.doContainerStartLevel(ModuleContainer.java:1760)
	at org.eclipse.osgi.container.SystemModule.stopWorker(SystemModule.java:275)
	at org.eclipse.osgi.internal.framework.EquinoxBundle$SystemBundle$EquinoxSystemModule.stopWorker(EquinoxBundle.java:202)
	at org.eclipse.osgi.container.Module.doStop(Module.java:658)
	at org.eclipse.osgi.container.Module.stop(Module.java:520)
	at org.eclipse.osgi.container.SystemModule.stop(SystemModule.java:207)
	at org.eclipse.osgi.internal.framework.EquinoxBundle$SystemBundle$EquinoxSystemModule$1.run(EquinoxBundle.java:220)
	at java.base/java.lang.Thread.run(Thread.java:834)

!ENTRY org.eclipse.jdt.ui 4 0 2020-05-31 01:23:19.399
--- 8< ---

And another one for the ResourcesPlugin:
--- 8< ---
!ENTRY org.eclipse.core.resources 4 0 2020-05-31 01:23:19.926
!MESSAGE FrameworkEvent ERROR
!STACK 0
org.osgi.framework.BundleException: Exception in org.eclipse.core.resources.ResourcesPlugin.stop() of bundle org.eclipse.core.resources.
	at org.eclipse.osgi.internal.framework.BundleContextImpl.stop(BundleContextImpl.java:919)
	at org.eclipse.osgi.internal.framework.EquinoxBundle.stopWorker0(EquinoxBundle.java:1029)
	at org.eclipse.osgi.internal.framework.EquinoxBundle$EquinoxModule.stopWorker(EquinoxBundle.java:370)
	at org.eclipse.osgi.container.Module.doStop(Module.java:658)
	at org.eclipse.osgi.container.Module.stop(Module.java:520)
	at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.decStartLevel(ModuleContainer.java:1885)
	at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.doContainerStartLevel(ModuleContainer.java:1760)
	at org.eclipse.osgi.container.SystemModule.stopWorker(SystemModule.java:275)
	at org.eclipse.osgi.internal.framework.EquinoxBundle$SystemBundle$EquinoxSystemModule.stopWorker(EquinoxBundle.java:202)
	at org.eclipse.osgi.container.Module.doStop(Module.java:658)
	at org.eclipse.osgi.container.Module.stop(Module.java:520)
	at org.eclipse.osgi.container.SystemModule.stop(SystemModule.java:207)
	at org.eclipse.osgi.internal.framework.EquinoxBundle$SystemBundle$EquinoxSystemModule$1.run(EquinoxBundle.java:220)
	at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: org.eclipse.core.runtime.AssertionFailedException: assertion failed: The application has not been initialized.
	at org.eclipse.core.runtime.Assert.isTrue(Assert.java:113)
	at org.eclipse.core.internal.runtime.InternalPlatform.assertInitialized(InternalPlatform.java:139)
	at org.eclipse.core.internal.runtime.InternalPlatform.getInstanceLocation(InternalPlatform.java:331)
	at org.eclipse.core.runtime.Plugin.savePluginPreferences(Plugin.java:341)
	at org.eclipse.core.resources.ResourcesPlugin.stop(ResourcesPlugin.java:458)
	at org.eclipse.osgi.internal.framework.BundleContextImpl$4.run(BundleContextImpl.java:899)
	at org.eclipse.osgi.internal.framework.BundleContextImpl$4.run(BundleContextImpl.java:1)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at org.eclipse.osgi.internal.framework.BundleContextImpl.stop(BundleContextImpl.java:891)
	... 13 more
Root exception:
org.eclipse.core.runtime.AssertionFailedException: assertion failed: The application has not been initialized.
	at org.eclipse.core.runtime.Assert.isTrue(Assert.java:113)
	at org.eclipse.core.internal.runtime.InternalPlatform.assertInitialized(InternalPlatform.java:139)
	at org.eclipse.core.internal.runtime.InternalPlatform.getInstanceLocation(InternalPlatform.java:331)
	at org.eclipse.core.runtime.Plugin.savePluginPreferences(Plugin.java:341)
	at org.eclipse.core.resources.ResourcesPlugin.stop(ResourcesPlugin.java:458)
	at org.eclipse.osgi.internal.framework.BundleContextImpl$4.run(BundleContextImpl.java:899)
	at org.eclipse.osgi.internal.framework.BundleContextImpl$4.run(BundleContextImpl.java:1)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at org.eclipse.osgi.internal.framework.BundleContextImpl.stop(BundleContextImpl.java:891)
	at org.eclipse.osgi.internal.framework.EquinoxBundle.stopWorker0(EquinoxBundle.java:1029)
	at org.eclipse.osgi.internal.framework.EquinoxBundle$EquinoxModule.stopWorker(EquinoxBundle.java:370)
	at org.eclipse.osgi.container.Module.doStop(Module.java:658)
	at org.eclipse.osgi.container.Module.stop(Module.java:520)
	at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.decStartLevel(ModuleContainer.java:1885)
	at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.doContainerStartLevel(ModuleContainer.java:1760)
	at org.eclipse.osgi.container.SystemModule.stopWorker(SystemModule.java:275)
	at org.eclipse.osgi.internal.framework.EquinoxBundle$SystemBundle$EquinoxSystemModule.stopWorker(EquinoxBundle.java:202)
	at org.eclipse.osgi.container.Module.doStop(Module.java:658)
	at org.eclipse.osgi.container.Module.stop(Module.java:520)
	at org.eclipse.osgi.container.SystemModule.stop(SystemModule.java:207)
	at org.eclipse.osgi.internal.framework.EquinoxBundle$SystemBundle$EquinoxSystemModule$1.run(EquinoxBundle.java:220)
	at java.base/java.lang.Thread.run(Thread.java:834)
--- 8< ---

This happens because:
 
AbstractUIPlugin -> getPlugin().savePluginPreferences() -> InternalPlatform.getInstanceLocation() -> InternalPlatform.assertInitialised
ResourcesPlugin.stop -> getPlugin().savePluginPreferences() -> InternalPlatform.getInstanceLocation() -> InternalPlatform.assertInitialised
Comment 1 Alex Blewitt CLA 2020-05-30 20:45:18 EDT
The fix probably looks something like:

diff --git bundles/org.eclipse.core.resources/src/org/eclipse/core/resources/ResourcesPlugin.java bundles/org.eclipse.core.resources/src/org/eclipse/core/resources/ResourcesPlugin.java
index 636b3a8c8..56770b27a 100644
--- bundles/org.eclipse.core.resources/src/org/eclipse/core/resources/ResourcesPlugin.java
+++ bundles/org.eclipse.core.resources/src/org/eclipse/core/resources/ResourcesPlugin.java
@@ -454,8 +454,6 @@ public final class ResourcesPlugin extends Plugin {
                if (workspaceRegistration != null) {
                        workspaceRegistration.unregister();
                }
-               // save the preferences for this plug-in
-               getPlugin().savePluginPreferences();
                workspace.close(null);
 
                // Forget workspace only if successfully closed, to

                
                
 And for the AbstractUIPlugin
 
 diff --git bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/plugin/AbstractUIPlugin.java bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/plugin/AbstractUIPlugin.java
index 087c67f829..022b509549 100644
--- bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/plugin/AbstractUIPlugin.java     
+++ bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/plugin/AbstractUIPlugin.java     
@@ -679,7 +679,6 @@ public abstract class AbstractUIPlugin extends Plugin {
                                context.removeBundleListener(bundleListener);
                        }
                        saveDialogSettings();
-                       savePreferenceStore();
                        preferenceStore = null;
                        if (imageRegistry != null)
                                imageRegistry.dispose();
Comment 2 Alex Blewitt CLA 2020-05-31 10:02:58 EDT
Alternatively the savePreference could be modified such that it handles the possibility of an assertion failure here:

	@Deprecated
	public final void savePluginPreferences() {
		Location instance = InternalPlatform.getDefault().getInstanceLocation();
		if (instance == null || !instance.isSet())
			// If the instance area is not set there is no point in getting or setting the preferences.
			// There is nothing to save in this case.
			return;

We could catch the exception generated from the internalPlatform, and if it's not running, return early.

Thoughts?
Comment 3 Andrey Loskutov CLA 2020-06-09 16:52:19 EDT
(In reply to Alex Blewitt from comment #2)
> Alternatively the savePreference could be modified such that it handles the
> possibility of an assertion failure here:
> 
> 	@Deprecated
> 	public final void savePluginPreferences() {
> 		Location instance = InternalPlatform.getDefault().getInstanceLocation();
> 		if (instance == null || !instance.isSet())
> 			// If the instance area is not set there is no point in getting or
> setting the preferences.
> 			// There is nothing to save in this case.
> 			return;
> 
> We could catch the exception generated from the internalPlatform, and if
> it's not running, return early.
> 
> Thoughts?

what says if(! Platform.isRunning()) ?

I would like to understand how can you reproduce the problem - it should happen every day with lot of users, but we haven't seen such reports yet. Or is this just theoretical discussion "what if..."?
Comment 4 Alex Blewitt CLA 2020-06-10 06:58:09 EDT
(In reply to Andrey Loskutov from comment #3)
> (In reply to Alex Blewitt from comment #2)
> > 	public final void savePluginPreferences() {
> > 		Location instance = InternalPlatform.getDefault().getInstanceLocation();
> 
> what says if(! Platform.isRunning()) ?

The call to InternalPlatform.getDefault().getInstanceLocation() is where the assert happens.

https://github.com/eclipse/eclipse.platform.runtime/blob/410c981210bdeb2a2c465cd47ff5a5e58e208be8/bundles/org.eclipse.core.runtime/src/org/eclipse/core/internal/runtime/InternalPlatform.java#L330-L333

My point is the code is defensively written such that if the instance location returns null then it leaves early, but the contract of getInstanceLocation() doesn't return null, it throws an assertion error in this case.

> I would like to understand how can you reproduce the problem - it should
> happen every day with lot of users, but we haven't seen such reports yet. Or
> is this just theoretical discussion "what if..."?

I managed to produce it when testing various scenarios, so I don't think it's common. It's really a stop-ordering problem (which is the inverse of a start-ordering problem). If you stop platform before you stop org.eclipse.common.runtime, and then you stop a bundle that descends from Plugin and has an activator, you'll see this assertion error happening.

I don't think you'll see this generally -- or if you do, the log entry will be swallowed because you are shutting down Eclipse, and by that point, the platform.log has gone away anyway.