[WIP] Bug 497444 - Progress indicator not updating in the splash screen
of Eclipse
Proof of concept
* simplifies the code
* remove unnecessary synchronize for the progress reporting of active
bundles
* Spins the event loop after
the splash creation to avoid a "ghost shell" (no content, I see the
widgets behind it).
* defines StartupProgressBundleListener as static inner class
* Trigger UI update if the startup listener is triggered for plug-in
loading
* Removes the loaded bundle name from the splash screen as this is
misleading for the user, the user will think that this plug-in takes
very long to load while it is actually already finished
* Assumes that 1/5 of the bundles have an early startup action
* Removes unnecessary null check as SubMonitor.convert() never returns
null
Change-Id: I2c5c60b596a07504d5906c302f0e7255a264f964
Signed-off-by: Lars Vogel <Lars.Vogel@vogella.com>
diff --git a/bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/Workbench.java b/bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/Workbench.java
index 8ef259e..981cf49 100644
--- a/bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/Workbench.java
+++ b/bundles/org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/Workbench.java
@@ -70,6 +70,7 @@
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.core.runtime.dynamichelpers.IExtensionTracker;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.e4.core.contexts.ContextFunction;
@@ -289,57 +290,25 @@
private static final String CMD_DATA = "-data"; //$NON-NLS-1$
private static final String CMD_VMARGS = "-vmargs"; //$NON-NLS-1$
- private final class StartupProgressBundleListener implements SynchronousBundleListener {
+ private static final class StartupProgressBundleListener implements SynchronousBundleListener {
- private final IProgressMonitor progressMonitor;
+ private final SubMonitor subMonitor;
+ private Display displayForStartupListener;
- private final int maximumProgressCount;
- // stack of names of bundles currently starting
- private final List<String> starting;
-
- StartupProgressBundleListener(IProgressMonitor progressMonitor, int maximumProgressCount) {
- super();
- this.progressMonitor = progressMonitor;
- this.maximumProgressCount = maximumProgressCount;
- this.starting = new ArrayList<>();
+ StartupProgressBundleListener(IProgressMonitor progressMonitor, Display display) {
+ displayForStartupListener = display;
+ subMonitor = SubMonitor.convert(progressMonitor);
+ subMonitor.setTaskName(WorkbenchMessages.Startup_Loading_Workbench);
}
@Override
public void bundleChanged(BundleEvent event) {
int eventType = event.getType();
- String bundleName;
- boolean worked = false;
- // Note: no calls to any non-trivial Eclipse code outside this class
- // should be made inside the synchronized block below. Such calls
- // can cause deadlocks on startup, see bug 502095.
- // Progress monitor calls *are* non-trivial.
- synchronized (this) {
- if (eventType == BundleEvent.STARTING) {
- starting.add(bundleName = event.getBundle().getSymbolicName());
- } else if (eventType == BundleEvent.STARTED) {
- progressCount++;
- if (progressCount <= maximumProgressCount) {
- worked = true;
- }
- int index = starting.lastIndexOf(event.getBundle().getSymbolicName());
- if (index >= 0) {
- starting.remove(index);
- }
- if (index != starting.size()) {
- return; // not currently displayed
- }
- bundleName = index == 0 ? null : (String) starting.get(index - 1);
- } else {
- return; // uninteresting event
- }
- }
- if (worked) {
- progressMonitor.worked(1);
- }
- if (bundleName != null) {
- String taskName = NLS.bind(WorkbenchMessages.Startup_Loading, bundleName);
- progressMonitor.subTask(taskName);
+ if (eventType == BundleEvent.STARTED) {
+ subMonitor.setWorkRemaining(5).worked(1);
+ spinEventQueueToUpdateUi(displayForStartupListener);
+
}
}
}
@@ -444,12 +413,6 @@
private final ServiceLocator serviceLocator;
/**
- * A count of how many plug-ins were loaded while restoring the workbench
- * state. Initially -1 for unknown number.
- */
- private int progressCount = -1;
-
- /**
* Listener list for registered IWorkbenchListeners .
*/
private ListenerList<IWorkbenchListener> workbenchListeners = new ListenerList<>(ListenerList.IDENTITY);
@@ -595,6 +558,10 @@
public static int createAndRunWorkbench(final Display display, final WorkbenchAdvisor advisor) {
final int[] returnCode = new int[1];
Realm.runWithDefault(DisplayRealm.getRealm(display), () -> {
+ spinEventQueueToUpdateUi(display);
+ boolean showProgress = PrefUtil.getAPIPreferenceStore()
+ .getBoolean(IWorkbenchPreferenceConstants.SHOW_PROGRESS_ON_STARTUP);
+
final String nlExtensions = Platform.getNLExtensions();
if (nlExtensions.length() > 0) {
ULocale.setDefault(Category.FORMAT,
@@ -629,25 +596,17 @@
// prime the splash nice and early
workbench.createSplashWrapper();
+ spinEventQueueToUpdateUi(display);
AbstractSplashHandler handler = getSplash();
- boolean showProgress = PrefUtil.getAPIPreferenceStore().getBoolean(
- IWorkbenchPreferenceConstants.SHOW_PROGRESS_ON_STARTUP);
- IProgressMonitor progressMonitor = null;
SynchronousBundleListener bundleListener = null;
if (handler != null && showProgress) {
- progressMonitor = handler.getBundleProgressMonitor();
- if (progressMonitor != null) {
- double cutoff = 0.95;
- int expectedProgressCount = Math.max(1, WorkbenchPlugin.getDefault()
- .getBundleCount() / 10);
- progressMonitor.beginTask("", expectedProgressCount); //$NON-NLS-1$
- bundleListener = workbench.new StartupProgressBundleListener(
- progressMonitor, (int) (expectedProgressCount * cutoff));
- WorkbenchPlugin.getDefault().addBundleListener(bundleListener);
- }
+ IProgressMonitor progressMonitor = SubMonitor.convert(handler.getBundleProgressMonitor());
+ bundleListener = new Workbench.StartupProgressBundleListener(progressMonitor, display);
+ WorkbenchPlugin.getDefault().addBundleListener(bundleListener);
}
+
setSearchContribution(appModel, true);
// run the legacy workbench once
returnCode[0] = workbench.runUI();
@@ -871,6 +830,7 @@
splash.init(splashShell);
}
+
@Override
public void handleException(Throwable e) {
StatusManager.getManager().handle(
@@ -886,6 +846,14 @@
SafeRunner.run(run);
}
+ private static void spinEventQueueToUpdateUi(final Display display) {
+ // Ensure that the splash screen is rendered
+ int safetyCounter = 0;
+ while (display.readAndDispatch() && safetyCounter++ < 100) {
+ // process until the queue is empty or until we hit the safetyCounter limit
+ }
+ }
+
/**
* Load an image from a filesystem path.
*
@@ -2491,10 +2459,7 @@
// assume we are loading a tenth of the installed plug-ins.
// (The Eclipse SDK loads 7 of 86 plug-ins at startup as of
// 2005-5-20)
- final int expectedProgressCount = Math.max(1, WorkbenchPlugin.getDefault()
- .getBundleCount() / 10);
-
- runStartupWithProgress(expectedProgressCount, () -> doOpenFirstTimeWindow());
+ runStartupWithProgress(() -> doOpenFirstTimeWindow());
}
}
@@ -2524,31 +2489,23 @@
}
}
- private void runStartupWithProgress(final int expectedProgressCount, final Runnable runnable) {
- progressCount = 0;
- final double cutoff = 0.95;
+ private void runStartupWithProgress(final Runnable runnable) {
AbstractSplashHandler handler = getSplash();
IProgressMonitor progressMonitor = null;
- if (handler != null)
+ if (handler != null) {
progressMonitor = handler.getBundleProgressMonitor();
+ }
if (progressMonitor == null) {
// cannot report progress (e.g. if the splash screen is not showing)
// fall back to starting without showing progress.
runnable.run();
} else {
- progressMonitor.beginTask("", expectedProgressCount); //$NON-NLS-1$
- SynchronousBundleListener bundleListener = new StartupProgressBundleListener(
- progressMonitor, (int) (expectedProgressCount * cutoff));
+ SynchronousBundleListener bundleListener = new StartupProgressBundleListener(progressMonitor, display);
WorkbenchPlugin.getDefault().addBundleListener(bundleListener);
try {
runnable.run();
- progressMonitor.subTask(WorkbenchMessages.Startup_Done);
- int remainingWork = expectedProgressCount
- - Math.min(progressCount, (int) (expectedProgressCount * cutoff));
- progressMonitor.worked(remainingWork);
- progressMonitor.done();
} finally {
WorkbenchPlugin.getDefault().removeBundleListener(bundleListener);
}