Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Watson2018-01-08 20:01:11 +0000
committerThomas Watson2018-01-10 21:02:59 +0000
commit5b4e5455cdc082859121c9c3b3e48bc491dad4c5 (patch)
treecc4dbfcb1ec27945469d9d3dcf33b1916cabd900
parent28fb2ea79c29fc21fb0c168192793e39a4ab187d (diff)
downloadrt.equinox.framework-5b4e5455cdc082859121c9c3b3e48bc491dad4c5.tar.gz
rt.equinox.framework-5b4e5455cdc082859121c9c3b3e48bc491dad4c5.tar.xz
rt.equinox.framework-5b4e5455cdc082859121c9c3b3e48bc491dad4c5.zip
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>
-rwxr-xr-xbundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/SystemBundleTests.java160
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleContainer.java68
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) {

Back to the top