diff options
author | Thomas Watson | 2018-01-08 20:01:11 +0000 |
---|---|---|
committer | Thomas Watson | 2018-01-10 21:02:59 +0000 |
commit | 5b4e5455cdc082859121c9c3b3e48bc491dad4c5 (patch) | |
tree | cc4dbfcb1ec27945469d9d3dcf33b1916cabd900 | |
parent | 28fb2ea79c29fc21fb0c168192793e39a4ab187d (diff) | |
download | rt.equinox.framework-5b4e5455cdc082859121c9c3b3e48bc491dad4c5.tar.gz rt.equinox.framework-5b4e5455cdc082859121c9c3b3e48bc491dad4c5.tar.xz rt.equinox.framework-5b4e5455cdc082859121c9c3b3e48bc491dad4c5.zip |
Bug 529639 Avoid extra sorts on start-level changesI20180117-2000I20180116-2000I20180115-2000I20180114-2000I20180113-1500I20180112-2000I20180111-2000I20180111-0530I20180110-2000
Check the timestamp of the module database,
if it has not changed do not resort the modules
each active framework start-level change.
Change-Id: I7b75999e72bbef1479d7143c08a92b456c7c6ef0
Signed-off-by: Thomas Watson <tjwatson@us.ibm.com>
2 files changed, 217 insertions, 11 deletions
diff --git a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/SystemBundleTests.java b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/SystemBundleTests.java index 3dc4f7ed0..bda5a641c 100755 --- a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/SystemBundleTests.java +++ b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/SystemBundleTests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008, 2017 IBM Corporation and others. + * Copyright (c) 2008, 2018 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -46,6 +46,7 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.jar.Attributes; import java.util.jar.JarEntry; @@ -75,6 +76,7 @@ import org.osgi.framework.BundleException; import org.osgi.framework.BundleListener; import org.osgi.framework.Constants; import org.osgi.framework.FrameworkEvent; +import org.osgi.framework.FrameworkListener; import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.ServiceReference; import org.osgi.framework.ServiceRegistration; @@ -86,6 +88,8 @@ import org.osgi.framework.hooks.weaving.WovenClass; import org.osgi.framework.launch.Framework; import org.osgi.framework.launch.FrameworkFactory; import org.osgi.framework.namespace.NativeNamespace; +import org.osgi.framework.startlevel.BundleStartLevel; +import org.osgi.framework.startlevel.FrameworkStartLevel; import org.osgi.framework.wiring.BundleCapability; import org.osgi.framework.wiring.BundleRequirement; import org.osgi.framework.wiring.BundleRevision; @@ -3306,6 +3310,160 @@ public class SystemBundleTests extends AbstractBundleTests { Assert.assertEquals("Wrong number of capabilities", capCount1, capCount2); } + public void testStartLevelSorting() throws IOException, InterruptedException { + File config = OSGiTestsActivator.getContext().getDataFile(getName()); //$NON-NLS-1$ + Map configuration = new HashMap(); + configuration.put(Constants.FRAMEWORK_STORAGE, config.getAbsolutePath()); + Equinox equinox = null; + final int numBundles = 200; + final File[] testBundleFiles = createBundles(new File(config, "testBundles"), numBundles); + try { + equinox = new Equinox(configuration); + equinox.start(); + final List<Bundle> testBundles = Collections.synchronizedList(new ArrayList<Bundle>()); + final List<Bundle> startedBundles = Collections.synchronizedList(new ArrayList<Bundle>()); + for (int i = 0; i < numBundles / 2; i++) { + Bundle b = equinox.getBundleContext().installBundle("reference:file:///" + testBundleFiles[i].getAbsolutePath()); + b.adapt(BundleStartLevel.class).setStartLevel(i + 2); + testBundles.add(b); + b.start(); + } + final Equinox finalEquinox = equinox; + + BundleListener initialListener = new SynchronousBundleListener() { + AtomicBoolean reverseStartLevel = new AtomicBoolean(); + AtomicBoolean installBundles = new AtomicBoolean(); + + @Override + public void bundleChanged(BundleEvent event) { + Bundle eBundle = event.getBundle(); + String bsn = eBundle.getSymbolicName(); + if (!bsn.startsWith("bundle-b") || event.getType() != BundleEvent.STARTED) { + return; + } + startedBundles.add(eBundle); + if (reverseStartLevel.compareAndSet(false, true)) { + for (int i = numBundles / 4, j = numBundles; i < numBundles / 2; i++, j--) { + BundleStartLevel tbsl = testBundles.get(i).adapt(BundleStartLevel.class); + tbsl.setStartLevel(j + 2 + numBundles); + } + } else if (installBundles.compareAndSet(false, true)) { + for (int i = numBundles / 2; i < numBundles; i++) { + try { + Bundle b = finalEquinox.getBundleContext().installBundle("reference:file:///" + testBundleFiles[i].getAbsolutePath()); + b.adapt(BundleStartLevel.class).setStartLevel(i + 2); + testBundles.add(b); + b.start(); + } catch (BundleException e) { + // do nothing + } + } + } + } + }; + + equinox.getBundleContext().addBundleListener(initialListener); + long startTime = System.currentTimeMillis(); + final CountDownLatch waitForStartLevel = new CountDownLatch(1); + equinox.adapt(FrameworkStartLevel.class).setStartLevel(numBundles * 3, new FrameworkListener() { + + @Override + public void frameworkEvent(FrameworkEvent event) { + waitForStartLevel.countDown(); + } + }); + waitForStartLevel.await(20, TimeUnit.SECONDS); + System.out.println("Start time: " + (System.currentTimeMillis() - startTime)); + + assertEquals("Did not finish start level setting.", 0, waitForStartLevel.getCount()); + assertEquals("Did not install all the expected bundles.", numBundles, testBundles.size()); + + List<Bundle> expectedStartOrder = new ArrayList<Bundle>(); + for (int i = 0; i < numBundles / 4; i++) { + expectedStartOrder.add(testBundles.get(i)); + } + for (int i = numBundles / 2; i < numBundles; i++) { + expectedStartOrder.add(testBundles.get(i)); + } + for (int i = (numBundles / 2) - 1; i >= numBundles / 4; i--) { + expectedStartOrder.add(testBundles.get(i)); + } + + assertEquals("Size on expected order is wrong.", numBundles, expectedStartOrder.size()); + for (int i = 0; i < numBundles; i++) { + assertEquals("Wrong bundle at: " + i, expectedStartOrder.get(i), startedBundles.get(i)); + } + + equinox.getBundleContext().removeBundleListener(initialListener); + + final List<Bundle> stoppedBundles = Collections.synchronizedList(new ArrayList<Bundle>()); + BundleListener shutdownListener = new SynchronousBundleListener() { + AtomicBoolean reverseStartLevel = new AtomicBoolean(); + AtomicBoolean uninstallBundles = new AtomicBoolean(); + AtomicBoolean inUninstall = new AtomicBoolean(); + + @Override + public void bundleChanged(BundleEvent event) { + if (inUninstall.get()) { + return; + } + Bundle eBundle = event.getBundle(); + String bsn = eBundle.getSymbolicName(); + if (!bsn.startsWith("bundle-b") || event.getType() != BundleEvent.STOPPED) { + return; + } + stoppedBundles.add(eBundle); + if (reverseStartLevel.compareAndSet(false, true)) { + for (int i = numBundles / 2, j = numBundles - 1; i < numBundles; i++, j--) { + BundleStartLevel tbsl = testBundles.get(i).adapt(BundleStartLevel.class); + tbsl.setStartLevel(j + 2); + } + } else if (uninstallBundles.compareAndSet(false, true)) { + for (int i = 0; i < numBundles / 4; i++) { + try { + inUninstall.set(true); + testBundles.get(i).uninstall(); + } catch (BundleException e) { + // do nothing + } finally { + inUninstall.set(false); + } + } + } + } + }; + equinox.getBundleContext().addBundleListener(shutdownListener); + + List<Bundle> expectedStopOrder = new ArrayList<Bundle>(expectedStartOrder); + Collections.reverse(expectedStopOrder); + Collections.reverse(expectedStopOrder.subList(numBundles / 4, 3 * (numBundles / 4))); + expectedStopOrder = new ArrayList(expectedStopOrder.subList(0, 3 * (numBundles / 4))); + + long stopTime = System.currentTimeMillis(); + equinox.stop(); + equinox.waitForStop(20000); + System.out.println("Stop time: " + (System.currentTimeMillis() - stopTime)); + + assertEquals("Size on expected order is wrong.", expectedStopOrder.size(), stoppedBundles.size()); + for (int i = 0; i < expectedStopOrder.size(); i++) { + assertEquals("Wrong bundle at: " + i, expectedStopOrder.get(i), stoppedBundles.get(i)); + } + } catch (BundleException e) { + fail("Failed init", e); + } finally { + try { + if (equinox != null) { + equinox.stop(); + equinox.waitForStop(1000); + } + } catch (BundleException e) { + fail("Failed to stop framework.", e); + } catch (InterruptedException e) { + fail("Failed to stop framework.", e); + } + } + } + // Disabled because the test is too much for the build machines public void disable_testBundleIDLock() { File config = OSGiTestsActivator.getContext().getDataFile(getName()); //$NON-NLS-1$ diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleContainer.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleContainer.java index 5756be216..62934f5b5 100644 --- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleContainer.java +++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleContainer.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2012, 2017 IBM Corporation and others. + * Copyright (c) 2012, 2018 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -12,16 +12,30 @@ package org.eclipse.osgi.container; import java.security.AccessController; import java.security.PrivilegedAction; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; -import org.eclipse.osgi.container.Module.*; +import org.eclipse.osgi.container.Module.StartOptions; +import org.eclipse.osgi.container.Module.State; +import org.eclipse.osgi.container.Module.StopOptions; import org.eclipse.osgi.container.ModuleContainerAdaptor.ContainerEvent; import org.eclipse.osgi.container.ModuleContainerAdaptor.ModuleEvent; import org.eclipse.osgi.container.ModuleDatabase.Sort; import org.eclipse.osgi.container.ModuleRequirement.DynamicModuleRequirement; -import org.eclipse.osgi.framework.eventmgr.*; +import org.eclipse.osgi.framework.eventmgr.CopyOnWriteIdentityMap; +import org.eclipse.osgi.framework.eventmgr.EventDispatcher; +import org.eclipse.osgi.framework.eventmgr.EventManager; +import org.eclipse.osgi.framework.eventmgr.ListenerQueue; import org.eclipse.osgi.framework.util.SecureAction; import org.eclipse.osgi.internal.container.InternalUtils; import org.eclipse.osgi.internal.container.LockSet; @@ -33,11 +47,24 @@ import org.eclipse.osgi.report.resolution.ResolutionReport.Entry; import org.eclipse.osgi.service.debug.DebugOptions; import org.eclipse.osgi.service.debug.DebugOptionsListener; import org.eclipse.osgi.util.NLS; -import org.osgi.framework.*; -import org.osgi.framework.namespace.*; +import org.osgi.framework.AdminPermission; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.BundleException; +import org.osgi.framework.Constants; +import org.osgi.framework.FrameworkListener; +import org.osgi.framework.Version; +import org.osgi.framework.namespace.HostNamespace; +import org.osgi.framework.namespace.IdentityNamespace; +import org.osgi.framework.namespace.PackageNamespace; import org.osgi.framework.startlevel.FrameworkStartLevel; -import org.osgi.framework.wiring.*; -import org.osgi.resource.*; +import org.osgi.framework.wiring.BundleCapability; +import org.osgi.framework.wiring.BundleRevision; +import org.osgi.framework.wiring.FrameworkWiring; +import org.osgi.resource.Namespace; +import org.osgi.resource.Requirement; +import org.osgi.resource.Resource; +import org.osgi.resource.Wire; import org.osgi.service.resolver.ResolutionException; /** @@ -1575,6 +1602,9 @@ public final class ModuleContainer implements DebugOptionsListener { } // Note that we must get a new list of modules each time; // this is because additional modules could have been installed from the previous start-level + // but only do this if the module database has changed!! + List<Module> sorted = null; + long currentTimestamp = Long.MIN_VALUE; if (newStartLevel > currentSL) { for (int i = currentSL; i < newStartLevel; i++) { int toStartLevel = i + 1; @@ -1582,7 +1612,16 @@ public final class ModuleContainer implements DebugOptionsListener { if (debugStartLevel) { Debug.println("StartLevel: incremented active start level to; " + toStartLevel); //$NON-NLS-1$ } - incStartLevel(toStartLevel, moduleDatabase.getSortedModules(Sort.BY_START_LEVEL)); + if (sorted == null || currentTimestamp != moduleDatabase.getTimestamp()) { + moduleDatabase.readLock(); + try { + sorted = moduleDatabase.getSortedModules(Sort.BY_START_LEVEL); + currentTimestamp = moduleDatabase.getTimestamp(); + } finally { + moduleDatabase.readUnlock(); + } + } + incStartLevel(toStartLevel, sorted); } } else { for (int i = currentSL; i > newStartLevel; i--) { @@ -1591,7 +1630,16 @@ public final class ModuleContainer implements DebugOptionsListener { if (debugStartLevel) { Debug.println("StartLevel: decremented active start level to " + toStartLevel); //$NON-NLS-1$ } - decStartLevel(toStartLevel, moduleDatabase.getSortedModules(Sort.BY_START_LEVEL, Sort.BY_DEPENDENCY)); + if (sorted == null || currentTimestamp != moduleDatabase.getTimestamp()) { + moduleDatabase.readLock(); + try { + sorted = moduleDatabase.getSortedModules(Sort.BY_START_LEVEL, Sort.BY_DEPENDENCY); + currentTimestamp = moduleDatabase.getTimestamp(); + } finally { + moduleDatabase.readUnlock(); + } + } + decStartLevel(toStartLevel, sorted); } } if (currentSL > 0 && newStartLevel > 0) { |