You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by pd...@apache.org on 2014/01/10 12:28:21 UTC
svn commit: r1557090 -
/felix/trunk/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/ServiceRaceTest.java
Author: pderop
Date: Fri Jan 10 11:28:21 2014
New Revision: 1557090
URL: http://svn.apache.org/r1557090
Log:
FELIX-3910: Added additional test for concurrent services registration (test not finished, I need to add configuration dependencies, as well as aspects ...).
Added:
felix/trunk/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/ServiceRaceTest.java
Added: felix/trunk/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/ServiceRaceTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/ServiceRaceTest.java?rev=1557090&view=auto
==============================================================================
--- felix/trunk/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/ServiceRaceTest.java (added)
+++ felix/trunk/dependencymanager/test/src/test/java/org/apache/felix/dm/test/integration/api/ServiceRaceTest.java Fri Jan 10 11:28:21 2014
@@ -0,0 +1,217 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.dm.test.integration.api;
+
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import javax.inject.Inject;
+
+import junit.framework.Assert;
+
+import org.apache.felix.dm.Component;
+import org.apache.felix.dm.DependencyManager;
+import org.apache.felix.dm.test.components.Ensure;
+import org.apache.felix.dm.test.integration.common.TestBase;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+/**
+ * FELIX-3910: Another race test for concurrent service registration/unregistration.
+ */
+@RunWith(PaxExam.class)
+public class ServiceRaceTest extends TestBase {
+ final static int SERVICES = 10;
+ final static int INVOKES = 10;
+ volatile ExecutorService m_execRegister; // used to register/unregister S services
+ volatile ExecutorService m_execInvoke; // Used by Client to invoke S services
+
+ @Inject
+ volatile ConfigurationAdmin m_ca;
+
+ @Test
+ public void testConcurrentServices() {
+ warn("starting concurrent services");
+ int cores = Math.max(10, Runtime.getRuntime().availableProcessors());
+ final DependencyManager dm = new DependencyManager(context);
+
+ try {
+ m_execRegister = Executors.newFixedThreadPool(cores);
+ m_execInvoke = Executors.newFixedThreadPool(1);
+
+ for (int loop = 0; loop < 10000; loop++) {
+ Ensure e = new Ensure(false);
+ long timeStamp = System.currentTimeMillis();
+
+ // Create one client depending on 'SERVICES' S services
+ Client client = new Client(e);
+ Component c = dm
+ .createComponent()
+ .setImplementation(client);
+ for (int i = 0; i < SERVICES; i ++) {
+ c.add(dm.createServiceDependency().setService(S.class, "(name=S" + i + ")").setRequired(true).setCallbacks(
+ "add", "remove"));
+ }
+ dm.add(c);
+
+ // Create all the 'SERVICES' S services concurrently
+ info("registering services concurrently");
+ final Ensure addE = new Ensure(false);
+ final List<Component> services = new CopyOnWriteArrayList<Component>();
+ for (int i = 0; i < SERVICES; i ++) {
+ final String name = "S" + i;
+ m_execRegister.execute(new Runnable() {
+ public void run() {
+ Hashtable h = new Hashtable();
+ h.put("name", name);
+ Component sImpl = dm
+ .createComponent()
+ .setImplementation(new SImpl())
+ .setInterface(S.class.getName(), h);
+ services.add(sImpl);
+ dm.add(sImpl);
+ addE.step();
+ }
+ });
+ }
+ addE.waitForStep(SERVICES, 5000);
+
+ // Make sure client is started:
+ e.waitForStep(1, 5000);
+
+ // Make sure client invoked services SERVICES * INVOKES times
+ e.waitForStep(1 + (SERVICES * INVOKES), 10000);
+ if ((loop+1) % 100 == 0) {
+ long duration = System.currentTimeMillis() - timeStamp;
+ warn("Performed %d tests in %d ms.", (loop+1), duration);
+ timeStamp = System.currentTimeMillis();
+ }
+
+ // Unregister services concurrently
+ final Ensure removeE = new Ensure(false);
+ info("unregistering services concurrently");
+ for (final Component sImpl : services) {
+ m_execRegister.execute(new Runnable() {
+ public void run() {
+ dm.remove(sImpl);
+ removeE.step();
+ }
+ });
+ }
+ removeE.waitForStep(SERVICES, 5000);
+
+ // Make sure Client has been stopped
+ info("waiting for client to be stopped");
+ int nextStep = 1 /* start */ + (SERVICES * INVOKES) + 1 /* stop */;
+ e.waitForStep(nextStep, 5000);
+
+ info("all services stopped");
+
+ // Make sure all services are all unbound from our client.
+ nextStep += SERVICES; // Client.removed should have been called for each unbound service.
+ e.waitForStep(nextStep, 5000);
+
+ // Clear everything before interating on next loop
+ dm.clear();
+
+ if (super.errorsLogged()) {
+ throw new IllegalStateException("Race test interrupted (some error occured, see previous logs)");
+ }
+ }
+ }
+
+ catch (Throwable t) {
+ error("Test failed", t);
+ Assert.fail("Test failed: " + t.getMessage());
+ }
+ finally {
+ shutdown(m_execRegister);
+ shutdown(m_execInvoke);
+ dm.clear();
+ }
+ }
+
+ void shutdown(ExecutorService exec) {
+ exec.shutdown();
+ try {
+ exec.awaitTermination(5, TimeUnit.SECONDS);
+ }
+ catch (InterruptedException e) {
+ }
+ }
+
+ public interface S {
+ void invoke(Ensure e);
+ }
+
+ public static class SImpl implements S {
+ public void invoke(Ensure e) {
+ e.step();
+ }
+ }
+
+ public class Client {
+ final Ensure m_e;
+ final Map<String, S> m_services = new ConcurrentHashMap<String, S>();
+
+ Client(Ensure e) {
+ m_e = e;
+ }
+
+ void add(Map<String, String> props, S s) {
+ info("client.add: %s (name=%s)", s, props.get("name"));
+ m_services.put(props.get("name"), s);
+ }
+
+ void remove(Map<String, String> props, S s) {
+ info("client.remove: %s (name=%s)", s, props.get("name"));
+ m_services.remove(props.get("name"));
+ m_e.step();
+ }
+
+ public void start() {
+ if (m_services.size() != SERVICES) {
+ error("Client started with unexpected number of injected services: %s", m_services);
+ return;
+ }
+ m_e.step(1);
+ m_execInvoke.execute(new Runnable() {
+ public void run() {
+ for (int i = 0; i < INVOKES; i ++) {
+ for (Map.Entry<String, S> e : m_services.entrySet()) {
+ e.getValue().invoke(m_e);
+ }
+ }
+ }
+ });
+ }
+
+ public void stop() {
+ m_e.step(1 /* start */ + (SERVICES * INVOKES) + 1);
+ }
+ }
+}