Bug 403872 - [Performance] Move the savePluginPreferences() call to background job to improve performance when saveEnabledStates() is invoked by activityManagerChanged() event.
Summary: [Performance] Move the savePluginPreferences() call to background job to impr...
Status: RESOLVED WONTFIX
Alias: None
Product: Platform
Classification: Eclipse Project
Component: UI (show other bugs)
Version: 3.4.2   Edit
Hardware: PC All
: P3 normal (vote)
Target Milestone: ---   Edit
Assignee: Platform UI Triaged CLA
QA Contact:
URL:
Whiteboard:
Keywords: helpwanted
Depends on:
Blocks:
 
Reported: 2013-03-20 06:10 EDT by ChunYan Zhang CLA
Modified: 2013-04-10 11:29 EDT (History)
7 users (show)

See Also:


Attachments
Code difference to remove WorkbenchPlugin.getDefault().savePluginPreferences() when saveEnabledStates is invoked in activityManagerChanged() (19.75 KB, text/html)
2013-03-21 05:54 EDT, Li Juan Gao CLA
no flags Details
Updated fix (code diff against Eclipse 3.4.2. Left: original, Right: fix) (55.28 KB, text/html)
2013-04-03 06:21 EDT, Li Juan Gao CLA
no flags Details
Source code of the fix (based on Eclipse 3.4.2) (11.13 KB, text/java)
2013-04-03 06:22 EDT, Li Juan Gao CLA
no flags Details
Source of the original code (Eclipse 3.4.2) (10.71 KB, text/java)
2013-04-03 06:23 EDT, Li Juan Gao CLA
no flags Details
patch for updated fix based on Eclipse 3.4.2, generated by diff command. (5.67 KB, text/plain)
2013-04-03 06:24 EDT, Li Juan Gao CLA
no flags Details
Possible fix for 3.4.2 (5.27 KB, patch)
2013-04-09 11:59 EDT, Dani Megert CLA
no flags Details | Diff
Fix to provide new API to determine whether save plugin preferences or not (11.78 KB, application/x-zip-compressed)
2013-04-10 11:24 EDT, Li Juan Gao CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description ChunYan Zhang CLA 2013-03-20 06:10:26 EDT
In Notes, when open a new tab or switch tab, IWorkbenchActivitySupport.setEnabledActivityIds() is called to set enabled activity ids. This call will indirectly invoke savePluginPreferences()(located in saveEnabledStates()) when ActivityPersistanceHelper activity listener response the activityManagerChanged event. The savePluginPreferences call in saveEnabledStates takes much time, which slow the open new tab and switch tab performance.

Can the WorkbenchPlugin.getDefault().savePluginPreferences() call be moved to background job? Or be removed when saveEnabledStates() is invoked in activityManagerChanged()? Since the saveEnabledStates() is invoked in shutdown(), is it necessary to save plugin preference in every activityManagerChanged() event?


org.eclipse.ui.workbench plugin
ActivityPersistanceHelper.java
public void activityManagerChanged(
                ActivityManagerEvent activityManagerEvent) {
            //process newly defined activities.
            if (activityManagerEvent.haveDefinedActivityIdsChanged()) {
                Set delta = new HashSet(activityManagerEvent
                        .getActivityManager().getDefinedActivityIds());
                delta.removeAll(activityManagerEvent
                        .getPreviouslyDefinedActivityIds());
                // whatever is still in delta are new activities - restore their
                // state
                loadEnabledStates(activityManagerEvent
                        .getActivityManager().getEnabledActivityIds(), delta);
            }
            if (activityManagerEvent.haveEnabledActivityIdsChanged()) {
				saveEnabledStates();
		}
        }

protected void saveEnabledStates() {
        try {
            saving = true;
	        
	        IPreferenceStore store = WorkbenchPlugin.getDefault()
	                .getPreferenceStore();
	
	        IWorkbenchActivitySupport support = PlatformUI.getWorkbench()
	                .getActivitySupport();
	        IActivityManager activityManager = support.getActivityManager();
	        Iterator values = activityManager.getDefinedActivityIds().iterator();
	        while (values.hasNext()) {
	            IActivity activity = activityManager.getActivity((String) values
	                    .next());
	            if (activity.getExpression() != null) {
	            	continue;
	            }
	
	            store.setValue(createPreferenceKey(activity.getId()), activity
	                    .isEnabled());
	        }
	        WorkbenchPlugin.getDefault().savePluginPreferences(); //move this to background job to improve performance when saveEnabledStates is invoked in activityManagerChanged(). or remove it when saveEnabledStates() is invoked in activityManagerChanged()? Since the saveEnabledStates() is invoked in shutdown(), is it necessary to save plugin preference in every activityManagerChanged() event?
        }
        finally {
            saving = false;
        }
    }
Comment 1 John Arthorne CLA 2013-03-20 16:48:49 EDT
It looks to me like this isn't something we need to persist to disk every time the active part changes. Persisting only during normal shutdown seems like the right answer to me. If the application crashes or exits without normal save, then these active activities won't be applicable in the next session anyway.

Also, making Plugin.savePluginPreferences asynchronous for all callers is quite a risky way to address an isolated performance problem.
Comment 2 Paul Webster CLA 2013-03-20 18:54:51 EDT
If interested, see http://wiki.eclipse.org/Platform_UI/How_to_Contribute

PW
Comment 3 Li Juan Gao CLA 2013-03-21 05:54:57 EDT
Created attachment 228828 [details]
Code difference to remove WorkbenchPlugin.getDefault().savePluginPreferences() when saveEnabledStates is invoked in activityManagerChanged()

Do not call WorkbenchPlugin.getDefault().savePluginPreferences() when saveEnabledStates is invoked in activityManagerChanged()
Comment 4 Li Juan Gao CLA 2013-03-21 06:13:51 EDT
From your comments, seems Plugin.savePluginPreferences is not necessary every time the active part changes, just required in shutdown().

We don't need to make Plugin.savePluginPreferences asynchronous for all callers, just need to remove it or move it to background when saveEnabledStates is called from activityManagerChanged()? While still keep Plugin.savePluginPreferences in UI thread as now in shutdown(). Is this change risky? 
I have attached the code difference of this change for reference.


This performance problem has widely impact to lots of Notes actions (from the important actions like Open Mail & Open Contact to the frequently used actions like Open Message, Send message, Send Meeting invitation, Reply, Compose, Switch tab etc.)

So it would greatly improve the overall Notes performance if the change can be made.


(In reply to comment #1)
> It looks to me like this isn't something we need to persist to disk every
> time the active part changes. Persisting only during normal shutdown seems
> like the right answer to me. If the application crashes or exits without
> normal save, then these active activities won't be applicable in the next
> session anyway.
> 
> Also, making Plugin.savePluginPreferences asynchronous for all callers is
> quite a risky way to address an isolated performance problem.
Comment 5 Paul Webster CLA 2013-03-21 06:29:29 EDT
Critical implies data loss, and that's not the case here.

PW
Comment 6 Paul Webster CLA 2013-03-21 06:34:47 EDT
(In reply to comment #3)
> Created attachment 228828 [details]
> Code difference to remove
> WorkbenchPlugin.getDefault().savePluginPreferences() when saveEnabledStates
> is invoked in activityManagerChanged()

See http://wiki.eclipse.org/Platform_UI/How_to_Contribute for acceptable formats to submit fixes.

PW
Comment 7 Li Juan Gao CLA 2013-03-26 06:20:38 EDT
any update?
Comment 8 Paul Webster CLA 2013-03-26 06:37:34 EDT
Right now we're waiting for you to submit a fix we can consume.  See http://wiki.eclipse.org/Platform_UI/How_to_Contribute


PW
Comment 9 Li Juan Gao CLA 2013-03-26 06:51:39 EDT
It is just my draft idea, i don't have fix.

Could Eclipse team investigate this and help to provide an official solution, and create a patch for Eclipse 3.4.2?

Thank you very much!
Comment 10 Li Juan Gao CLA 2013-03-27 06:21:08 EDT
This performance improvement is needed by Notes. Can Eclipse team provide an ETA for the patch?

Thanks!
Comment 11 Paul Webster CLA 2013-03-27 07:58:07 EDT
If you don't have a fix then then this bug won't be looked at.  We don't support 3.4.x any more.


PW
Comment 12 Li Juan Gao CLA 2013-03-27 09:07:54 EDT
The code difference I attached is the proposed fix. Can anyone evaluate it? Is it possible to create patch for Eclipse 4.0?
Comment 13 Paul Webster CLA 2013-03-27 09:44:29 EDT
(In reply to comment #12)
> The code difference I attached is the proposed fix. Can anyone evaluate it?
> Is it possible to create patch for Eclipse 4.0?

If that is a proposed fix, that's great.  You need to follow the steps at http://wiki.eclipse.org/Platform_UI/How_to_Contribute to either push the fix to Gerrit for review or attach a patch here.

PW
Comment 14 Li Juan Gao CLA 2013-04-03 06:17:27 EDT
Remove the save activity preferences call every time active part change may be too aggressive. So we propose an updated fix to move that call to background (move the  Plugin.savePluginPreferences() to background when saveEnabledStates is invoked in activityManagerChanged()).

Please find the updated fix and related files in attachments.

The patch is based on the src code of Eclipse 3.4.2 and generated by diff command.
Comment 15 Li Juan Gao CLA 2013-04-03 06:21:34 EDT
Created attachment 229275 [details]
Updated fix (code diff against Eclipse 3.4.2. Left: original, Right: fix)

Code difference file based on Eclipse 3.4.2. Left column: original code, Right column: fix
Comment 16 Li Juan Gao CLA 2013-04-03 06:22:30 EDT
Created attachment 229276 [details]
Source code of the fix (based on Eclipse 3.4.2)
Comment 17 Li Juan Gao CLA 2013-04-03 06:23:14 EDT
Created attachment 229277 [details]
Source of the original code (Eclipse 3.4.2)
Comment 18 Li Juan Gao CLA 2013-04-03 06:24:13 EDT
Created attachment 229278 [details]
patch for updated fix based on Eclipse 3.4.2, generated by diff command.
Comment 19 Paul Webster CLA 2013-04-04 09:15:47 EDT
(In reply to comment #3)
> Created attachment 228828 [details]
> Code difference to remove
> WorkbenchPlugin.getDefault().savePluginPreferences() when saveEnabledStates
> is invoked in activityManagerChanged()

Li, I've turned this into a Gerrit change: https://git.eclipse.org/r/#/c/11638/

You can get your patch from that if you want, and if it fixes your problem then it'll be merged into Kepler and can be patched back to 4.2.2+

PW
Comment 20 Paul Webster CLA 2013-04-05 08:00:08 EDT
(In reply to comment #19)
> Li, I've turned this into a Gerrit change:
> https://git.eclipse.org/r/#/c/11638/
> 
> You can get your patch from that if you want, and if it fixes your problem
> then it'll be merged into Kepler and can be patched back to 4.2.2+

Dani has pointed out that this prevents saving the preferences on explicit calls to preference page.

Please post a stack trace of the call that persists the preference that is causing your problem (happening when you open a new tab or switch tabs as you said in comment #0 )

PW
Comment 21 Li Juan Gao CLA 2013-04-08 02:05:35 EDT
The patch you merged was obsoleted as we think it is aggressive and may be risky.

A new patch was attached to move WorkbenchPlugin.getDefault().savePluginPreferences() to background rather than removing it.

Could you please merge the updated patch (https://bugs.eclipse.org/bugs/attachment.cgi?id=229275) and get it reviewed?

The original Eclipse code (https://bugs.eclipse.org/bugs/attachment.cgi?id=229277) and the fix code (https://bugs.eclipse.org/bugs/attachment.cgi?id=229276) were also attached for your comparison.
Comment 22 Dani Megert CLA 2013-04-08 05:01:22 EDT
(In reply to comment #21)
> The patch you merged was obsoleted as we think it is aggressive and may be
> risky.
> 
> A new patch was attached to move
> WorkbenchPlugin.getDefault().savePluginPreferences() to background rather
> than removing it.
> 
> Could you please merge the updated patch
> (https://bugs.eclipse.org/bugs/attachment.cgi?id=229275) and get it reviewed?
> 
> The original Eclipse code
> (https://bugs.eclipse.org/bugs/attachment.cgi?id=229277) and the fix code
> (https://bugs.eclipse.org/bugs/attachment.cgi?id=229276) were also attached
> for your comparison.

Putting the logic into the background and synchronize the code is not safer than what Paul proposed.

Can you please attach the stack trace requested by Paul in comment 20. Thanks.
Comment 23 Li Juan Gao CLA 2013-04-08 10:50:46 EDT
(In reply to comment #22)
> (In reply to comment #21)
> The patch you merged was obsoleted as we think
> it is aggressive and may be
> risky.
> 
> A new patch was attached to move
>
> WorkbenchPlugin.getDefault().savePluginPreferences() to background rather
>
> than removing it.
> 
> Could you please merge the updated patch
>
> (https://bugs.eclipse.org/bugs/attachment.cgi?id=229275) and get it
> reviewed?
> 
> The original Eclipse code
>
> (https://bugs.eclipse.org/bugs/attachment.cgi?id=229277) and the fix code
>
> (https://bugs.eclipse.org/bugs/attachment.cgi?id=229276) were also attached
> > for your comparison.

Putting the logic into the background and
> synchronize the code is not safer than what Paul proposed.

Can you please
> attach the stack trace requested by Paul in comment 20. Thanks.

Please find below the stack trace:
New_configuration [Client Services]	
	org.eclipse.equinox.launcher.Main at localhost:51197	
		Thread [main] (Suspended (breakpoint at line 268 in ActivityPersistanceHelper))	
			ActivityPersistanceHelper.saveEnabledStates() line: 268	
			ActivityPersistanceHelper$1.activityManagerChanged(ActivityManagerEvent) line: 75	
			ProxyActivityManager(AbstractActivityManager).fireActivityManagerChanged(ActivityManagerEvent) line: 52	
			ProxyActivityManager$1.activityManagerChanged(ActivityManagerEvent) line: 50	
			MutableActivityManager(AbstractActivityManager).fireActivityManagerChanged(ActivityManagerEvent) line: 52	
			MutableActivityManager.updateListeners(boolean, Map, Set, Set) line: 625	
			MutableActivityManager.setEnabledActivityIds(Set) line: 592	
			WorkbenchActivitySupport.setEnabledActivityIds(Set) line: 320	
			ActivityController.update() line: 203	
			PerspectiveListener.perspectiveActivated(IWorkbenchPage, IPerspectiveDescriptor) line: 321	
			PerspectiveListenerList$1.run() line: 75	
			SafeRunner.run(ISafeRunnable) line: 37	
			Platform.run(ISafeRunnable) line: 880	
			PerspectiveListenerList.fireEvent(SafeRunnable, IPerspectiveListener, IPerspectiveDescriptor, String) line: 58	
			PerspectiveListenerList.firePerspectiveActivated(IWorkbenchPage, IPerspectiveDescriptor) line: 73	
			WorkbenchWindow.firePerspectiveActivated(IWorkbenchPage, IPerspectiveDescriptor) line: 1330	
			WorkbenchPage.setPerspective(Perspective) line: 3680	
			WorkbenchPage.busySetPerspective(IPerspectiveDescriptor) line: 1032	
			WorkbenchPage.access$16(WorkbenchPage, IPerspectiveDescriptor) line: 1016	
			WorkbenchPage$18.run() line: 3801	
			BusyIndicator.showWhile(Display, Runnable) line: 70	
			WorkbenchPage.setPerspective(IPerspectiveDescriptor) line: 3799	
			PerspectiveAction.run() line: 140	
			TabFolderContributionItem.handleWidgetSelection(SelectionEvent, boolean) line: 451	
			TabFolderContributionItem.access$3(TabFolderContributionItem, SelectionEvent, boolean) line: 430	
			TabFolderContributionItem$3.widgetSelected(SelectionEvent) line: 205	
			TypedListener.handleEvent(Event) line: 240	
			EventTable.sendEvent(Event) line: 84	
			STabFolder(Widget).sendEvent(Event) line: 1053	
			STabFolder(Widget).sendEvent(int, Event, boolean) line: 1077	
			STabFolder(Widget).sendEvent(int, Event) line: 1062	
			STabFolder(Widget).notifyListeners(int, Event) line: 774	
			STabFolder.setSelection(int, boolean) line: 3272	
			STabFolder.onMouse(Event) line: 1991	
			STabFolder$1.handleEvent(Event) line: 230	
			EventTable.sendEvent(Event) line: 84	
			STabFolder(Widget).sendEvent(Event) line: 1053	
			Display.runDeferredEvents() line: 4166	
			Display.readAndDispatch() line: 3755	
			Workbench.runEventLoop(Window$IExceptionHandler, Display) line: 2384	
			Workbench.runUI() line: 2348	
			Workbench.access$4(Workbench) line: 2200	
			Workbench$5.run() line: 495	
			Realm.runWithDefault(Realm, Runnable) line: 288	
			Workbench.createAndRunWorkbench(Display, WorkbenchAdvisor) line: 490	
			PlatformUI.createAndRunWorkbench(Display, WorkbenchAdvisor) line: 149	
			RCPApplication.run(Object) line: 67	
			NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]	
			NativeMethodAccessorImpl.invoke(Object, Object[]) line: 60	
			DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 37	
			Method.invoke(Object, Object...) line: 611	
			EclipseAppContainer.callMethodWithException(Object, String, Class[], Object[]) line: 574	
			EclipseAppHandle.run(Object) line: 195	
			EclipseAppLauncher.runApplication(Object) line: 110	
			EclipseAppLauncher.start(Object) line: 79	
			EclipseStarter.run(Object) line: 386	
			EclipseStarter.run(String[], Runnable) line: 179	
			NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]	
			NativeMethodAccessorImpl.invoke(Object, Object[]) line: 60	
			DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 37	
			Method.invoke(Object, Object...) line: 611	
			Main.invokeFramework(String[], URL[]) line: 564	
			Main.basicRun(String[]) line: 504	
			Main.run(String[]) line: 1251	
			Main.main(String[]) line: 1227	
		Daemon Thread [Signal Dispatcher] (Running)	
		Daemon Thread [Attach API wait loop] (Running)	
		Thread [OSGi Console] (Running)	
		Daemon Thread [State Data Manager] (Running)	
		Daemon Thread [Log Event Dispatcher] (Running)	
		Daemon Thread [Framework Event Dispatcher] (Running)	
		Daemon Thread [Start Level Event Dispatcher] (Running)	
		Daemon Thread [Thread-7] (Running)	
		Thread [Worker-0] (Running)	
		Thread [Worker-1] (Running)	
		Thread [pool-1-thread-1] (Running)		
	D:\Lotus\Notes\jvm\bin\javaw.exe (Apr 8, 2013 7:16:58 PM)
Comment 24 Dani Megert CLA 2013-04-08 12:36:33 EDT
Side note from a chat with Li Juan: the product's actions cause perspective activations and hence this code is triggered frequently.

The stack trace shows that the #setEnabledActivityIds(Set) is called by the product. Hence, the suggestion I made, to simply move the save from the listener to that method, won't do the trick.


The safest fix, which does not affect other clients, is to
- document that IWorkbenchActivitySupport#setEnabledActivityIds(Set) saves
  the plug-in preferences.

- add a new API IWorkbenchActivitySupport#setEnabledActivityIds(Set, boolean)
  which allows to specify whether to save or not. The product can call this
  new method, requesting no save. 

- explicitly do the save in the implementation (WorkbenchActivitySupport) 
  depending on the flag.

- no longer save in the listener (covered by Paul's change)


If we all agree on this, I can upload a patch for testing and review.
Comment 25 Li Juan Gao CLA 2013-04-09 05:54:34 EDT
(In reply to comment #24)
> Side note from a chat with Li Juan: the product's actions cause perspective
> activations and hence this code is triggered frequently.
> 
> The stack trace shows that the #setEnabledActivityIds(Set) is called by the
> product. Hence, the suggestion I made, to simply move the save from the
> listener to that method, won't do the trick.
> 
> 
> The safest fix, which does not affect other clients, is to
> - document that IWorkbenchActivitySupport#setEnabledActivityIds(Set) saves
>   the plug-in preferences.
> 
> - add a new API IWorkbenchActivitySupport#setEnabledActivityIds(Set, boolean)
>   which allows to specify whether to save or not. The product can call this
>   new method, requesting no save. 
> 
> - explicitly do the save in the implementation (WorkbenchActivitySupport) 
>   depending on the flag.
> 
> - no longer save in the listener (covered by Paul's change)
> 
> 
> If we all agree on this, I can upload a patch for testing and review.

Thanks Deni. This looks good to me. It is much safer.
Comment 26 ChunYan Zhang CLA 2013-04-09 06:02:25 EDT
Thanks Dani. The fix looks good for us. Could you please help to create a patch based on Eclipse 3.4.2 because our product is still using Eclipse 3.4.2?

If you can't create a patch based on Eclipse 3.4.2, can you help to send us the src code and diff files of your changes? Then we can refer them to merge the fix into our product. Thanks very much.

(In reply to comment #24)
> Side note from a chat with Li Juan: the product's actions cause perspective
> activations and hence this code is triggered frequently.
> 
> The stack trace shows that the #setEnabledActivityIds(Set) is called by the
> product. Hence, the suggestion I made, to simply move the save from the
> listener to that method, won't do the trick.
> 
> 
> The safest fix, which does not affect other clients, is to
> - document that IWorkbenchActivitySupport#setEnabledActivityIds(Set) saves
>   the plug-in preferences.
> 
> - add a new API IWorkbenchActivitySupport#setEnabledActivityIds(Set, boolean)
>   which allows to specify whether to save or not. The product can call this
>   new method, requesting no save. 
> 
> - explicitly do the save in the implementation (WorkbenchActivitySupport) 
>   depending on the flag.
> 
> - no longer save in the listener (covered by Paul's change)
> 
> 
> If we all agree on this, I can upload a patch for testing and review.
Comment 27 Dani Megert CLA 2013-04-09 06:39:16 EDT
(In reply to comment #26)
> Thanks Dani. The fix looks good for us. Could you please help to create a
> patch based on Eclipse 3.4.2 because our product is still using Eclipse
> 3.4.2?
> 

I'm waiting for an OK from Paul before I start to work on a patch.
Comment 28 Paul Webster CLA 2013-04-09 07:22:16 EDT
(In reply to comment #24)
> The safest fix, which does not affect other clients, is to
> - document that IWorkbenchActivitySupport#setEnabledActivityIds(Set) saves
>   the plug-in preferences.
> 
> - add a new API IWorkbenchActivitySupport#setEnabledActivityIds(Set, boolean)
>   which allows to specify whether to save or not. The product can call this
>   new method, requesting no save. 
> 
> - explicitly do the save in the implementation (WorkbenchActivitySupport) 
>   depending on the flag.
> 
> - no longer save in the listener (covered by Paul's change)

OK Dani, sounds good.

PW
Comment 29 Dani Megert CLA 2013-04-09 11:59:15 EDT
Created attachment 229515 [details]
Possible fix for 3.4.2

Further information revealed that the problem has only been seen in performance tests and that the delay (30 ms on the test machine, 3 ms on my fast machine) is not something that's noticeable by an end user and hence not worth fixing.

This patch implements the solution from comment 24 with one simplification: the call to save the plug-in preferences can be entirely removed from ActivityPersistanceHelper#saveEnabledStates because the framework saves the plug-in preference anyway on shutdown, when the workbench bundle gets stopped.

Feel free to use it in your product.
Comment 30 Dani Megert CLA 2013-04-09 11:59:28 EDT
.
Comment 31 Dani Megert CLA 2013-04-10 09:10:07 EDT
Comment on attachment 229515 [details]
Possible fix for 3.4.2

As correctly noticed by Li Juan, this patch can result in more save calls than before, e.g. when the activities did not change. Hence, it should not be used.

Given that the product manages the activities dynamically and does not surface the 'Capabilities' preference page, the easiest solution for that specific product is just to remove the save call entirely. As mentioned before, the framework will save the plug-in preferences during shutdown.
Comment 32 Li Juan Gao CLA 2013-04-10 11:24:12 EDT
Created attachment 229569 [details]
Fix to provide new API to determine whether save plugin preferences or not

Fix to provide a new API to determine whether save the plugin preferences, which just take effect for callers of this new API. 
There is no change/impact to existing code, that is, the save call is still retained to protect against crash for existing code.

Very much appreciated if someone can help review the changes. Thanks!
Comment 33 Dani Megert CLA 2013-04-10 11:29:31 EDT
(In reply to comment #32)
> Created attachment 229569 [details]
> Fix to provide new API to determine whether save plugin preferences or not
> 
> Fix to provide a new API to determine whether save the plugin preferences,
> which just take effect for callers of this new API. 
> There is no change/impact to existing code, that is, the save call is still
> retained to protect against crash for existing code.
> 
> Very much appreciated if someone can help review the changes. Thanks!

Sorry, I won't have time for this, especially since the suggested solution is fine for your product, (which does not show the 'Capability' preference page) and it is essentially the same as suggested by Paul.