felix-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From pde...@apache.org
Subject svn commit: r1571864 [5/5] - in /felix/sandbox/pderop/dependencymanager-prototype: ./ cnf/ cnf/bin/ cnf/buildrepo/ cnf/buildrepo/biz.aQute.junit/ cnf/buildrepo/biz.aQute.launcher/ cnf/buildrepo/ee.foundation/ cnf/buildrepo/ee.minimum/ cnf/buildrepo/jun...
Date Tue, 25 Feb 2014 23:28:11 GMT
Added: felix/sandbox/pderop/dependencymanager-prototype/dm/test/test/SerialExecutorTest.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm/test/test/SerialExecutorTest.java?rev=1571864&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm/test/test/SerialExecutorTest.java
(added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm/test/test/SerialExecutorTest.java
Tue Feb 25 23:28:08 2014
@@ -0,0 +1,115 @@
+package test;
+
+import java.util.Random;
+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.AtomicReference;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import dm.impl.Logger;
+import dm.impl.SerialExecutor;
+
+/**
+ * Validates SerialExecutor used by DM implementation.
+ */
+public class SerialExecutorTest extends TestBase {
+    final Random m_rnd = new Random();
+    final int TESTS = 100000;
+    
+    @Test
+    public void testSerialExecutor() {
+        info("Testing serial executor");
+        int cores = Math.max(10, Runtime.getRuntime().availableProcessors());
+        ExecutorService threadPool = null;
+
+        try {
+            threadPool = Executors.newFixedThreadPool(cores);
+            final SerialExecutor serial = new SerialExecutor(new Logger(null));
+
+            long timeStamp = System.currentTimeMillis();
+            for (int i = 0; i < TESTS; i++) {
+                final CountDownLatch latch = new CountDownLatch(cores * 2 /* each task reexecutes
itself one time */);
+                final SerialTask task = new SerialTask(serial, latch);
+                for (int j = 0; j < cores; j ++) {
+                    threadPool.execute(new Runnable() {
+                        public void run() {
+                            serial.execute(task);
+                        }
+                    });
+                }
+                Assert.assertTrue("Test " + i + " did not terminate timely", latch.await(20000,
TimeUnit.MILLISECONDS));
+            }
+            long now = System.currentTimeMillis();
+            System.out.println("Performed " + TESTS + " tests in " + (now - timeStamp) +
" ms.");
+            timeStamp = now;
+        }
+
+        catch (Throwable t) {
+            t.printStackTrace();
+            Assert.fail("Test failed: " + t.getMessage());
+        }
+        finally {
+            shutdown(threadPool);
+        }
+    }
+
+    void shutdown(ExecutorService exec) {
+        exec.shutdown();
+        try {
+            exec.awaitTermination(5, TimeUnit.SECONDS);
+        }
+        catch (InterruptedException e) {
+        }
+    }
+
+    class SerialTask implements Runnable {
+        final AtomicReference<Thread> m_executingThread = new AtomicReference<Thread>();
+        final CountDownLatch m_latch;
+        private boolean m_firstExecution;
+        private final SerialExecutor m_exec;          
+        
+        SerialTask(SerialExecutor exec, CountDownLatch latch) {
+            m_latch = latch;
+            m_exec = exec;
+            m_firstExecution = true;
+        }
+        
+        public void run() {
+            Thread self = Thread.currentThread();
+            if (m_firstExecution) {
+                // The first time we are executed, the previous executing thread stored in
our m_executingThread should be null
+                if (!m_executingThread.compareAndSet(null, self)) {
+                    System.out.println("detected concurrent call to SerialTask: currThread="
+ self
+                        + ", other executing thread=" + m_executingThread);
+                    return;
+                }
+            } else {
+                // The second time we are executed, the previous executing thread stored
in our m_executingThread should be 
+                // the current running thread.
+                if (m_executingThread.get() != self) {
+                    System.out.println("expect to execute reentrant tasks in same thread,
but current thread=" + self
+                        + ", while expected is " + m_executingThread);
+                    return;
+                }
+            }
+                        
+            if (m_firstExecution) {
+                m_firstExecution = false;
+                m_exec.execute(this); // Our run method must be called immediately
+            } else {
+                if (! m_executingThread.compareAndSet(self, null)) {
+                    System.out.println("detected concurrent call to SerialTask: currThread="
+ self
+                        + ", other executing thread=" + m_executingThread);
+                    return;                    
+                }
+                m_firstExecution = true;
+            }
+            
+            m_latch.countDown();
+        }
+    }
+}

Added: felix/sandbox/pderop/dependencymanager-prototype/dm/test/test/ServiceRaceTest.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm/test/test/ServiceRaceTest.java?rev=1571864&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm/test/test/ServiceRaceTest.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm/test/test/ServiceRaceTest.java Tue
Feb 25 23:28:08 2014
@@ -0,0 +1,233 @@
+package test;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.osgi.service.cm.ConfigurationException;
+
+import dm.Component;
+import dm.ComponentStateListener;
+import dm.Dependency;
+import dm.context.ComponentState;
+import dm.context.Event;
+import dm.impl.ComponentImpl;
+import dm.impl.ConfigurationDependencyImpl;
+import dm.impl.DependencyImpl;
+import dm.impl.EventImpl;
+
+/**
+ * This test class simulates a client having many dependencies being registered/unregistered
concurrently.
+ */
+public class ServiceRaceTest extends TestBase {
+    final static int STEP_WAIT = 5000;
+    final static int DEPENDENCIES = 10;
+    final static int LOOPS = 10000;
+
+    // Executor used to bind/unbind service dependencies.
+    ExecutorService m_threadpool;
+    // Timestamp used to log the time consumed to execute 100 tests.
+    long m_timeStamp;
+
+    /**
+     * Creates many service dependencies, and activate/deactivate them concurrently.  
+     */
+    @Test
+    public void createParallelComponentRegistgrationUnregistration() {
+        info("Starting createParallelComponentRegistgrationUnregistration test");
+        int cores = Math.max(16, Runtime.getRuntime().availableProcessors());
+        info("using " + cores + " cores.");
+
+        m_threadpool = Executors.newFixedThreadPool(Math.max(cores, DEPENDENCIES + 3 /* start/stop/configure
*/));
+
+        try {
+            m_timeStamp = System.currentTimeMillis();
+            for (int loop = 0; loop < LOOPS; loop++) {
+                doTest(loop);
+            }
+        }
+        catch (Throwable t) {
+            warn("got unexpected exception", t);
+        }
+        finally {
+            shutdown(m_threadpool);
+        }
+    }
+
+    void shutdown(ExecutorService exec) {
+        exec.shutdown();
+        try {
+            exec.awaitTermination(5, TimeUnit.SECONDS);
+        }
+        catch (InterruptedException e) {
+        }
+    }
+
+    void doTest(int loop) throws Throwable {
+        debug("loop#%d -------------------------", loop);
+
+        final Ensure step = new Ensure(false);
+
+        // Create one client component, which depends on many service dependencies
+        final ComponentImpl client = new ComponentImpl();
+        final Client theClient = new Client(step);
+        client.setImplementation(theClient);
+
+        // Create client service dependencies
+        final DependencyImpl[] dependencies = new DependencyImpl[DEPENDENCIES];
+        for (int i = 0; i < DEPENDENCIES; i++) {
+            dependencies[i] = new DependencyImpl();
+            dependencies[i].setRequired(true);
+            dependencies[i].setCallbacks("add", "remove");
+            client.add(dependencies[i]);
+        }
+        final ConfigurationDependencyImpl confDependency = new ConfigurationDependencyImpl();
+        client.add(confDependency);
+
+        // Create Configuration (concurrently).
+        // We have to simulate the configuration update, using a component state listener,
which will
+        // trigger an update thread, but only once the component is started.
+        final ComponentStateListener listener = new ComponentStateListener() {
+            private volatile Dictionary m_conf;
+
+            public void changed(ComponentState state) {
+                if (state == ComponentState.WAITING_FOR_REQUIRED && m_conf == null)
{
+                    m_conf = new Hashtable();
+                    m_conf.put("foo", "bar");
+                    m_threadpool.execute(new Runnable() {
+                        public void run() {
+                            try {
+                                confDependency.updated(m_conf);
+                            }
+                            catch (ConfigurationException e) {
+                                warn("configuration failed", e);
+                            }
+                        }
+                    });
+                }
+            }
+        };
+        client.add(listener);
+
+        // Activate the client service dependencies concurrently.
+        for (int i = 0; i < DEPENDENCIES; i++) {
+            final DependencyImpl dep = dependencies[i];
+            final Event added = new EventImpl(i);
+            m_threadpool.execute(new Runnable() {
+                public void run() {
+                    dep.add(added);
+                }
+            });
+        }
+
+        // Start the client (concurrently)
+        m_threadpool.execute(new Runnable() {
+            public void run() {
+                client.start();
+            }
+        });
+
+        // Ensure that client has been started.
+        int expectedStep = 1 /* conf */ + DEPENDENCIES + 1 /* start */;
+        step.waitForStep(expectedStep, STEP_WAIT);
+        Assert.assertEquals(DEPENDENCIES, theClient.getDependencies());
+        Assert.assertNotNull(theClient.getConfiguration());
+        client.remove(listener);
+
+        // Stop the client and all dependencies concurrently.
+        for (int i = 0; i < DEPENDENCIES; i++) {
+            final DependencyImpl dep = dependencies[i];
+            final Event removed = new EventImpl(i);
+            m_threadpool.execute(new Runnable() {
+                public void run() {
+                    dep.remove(removed);
+                }
+            });
+        }
+        m_threadpool.execute(new Runnable() {
+            public void run() {
+                client.stop();
+            }
+        });
+        m_threadpool.execute(new Runnable() {
+            public void run() {
+                try {
+                    confDependency.updated(null);
+                }
+                catch (ConfigurationException e) {
+                    warn("error while unconfiguring", e);
+                }
+            }
+        });
+
+        // Ensure that client has been stopped, then destroyed, then unbound from all dependencies
+        expectedStep += 2; // stop/destroy
+        expectedStep += DEPENDENCIES; // removed all dependencies
+        expectedStep += 1; // removed configuration
+        step.waitForStep(expectedStep, STEP_WAIT);
+        step.ensure();
+        Assert.assertEquals(0, theClient.getDependencies());
+        Assert.assertNull(theClient.getConfiguration());
+
+        debug("finished one test loop");
+        if ((loop + 1) % 100 == 0) {
+            long duration = System.currentTimeMillis() - m_timeStamp;
+            warn("Performed 100 tests (total=%d) in %d ms.", (loop + 1), duration);
+            m_timeStamp = System.currentTimeMillis();
+        }
+    }
+
+    public class Client {
+        final Ensure m_step;
+        volatile int m_dependencies;
+        volatile Dictionary m_conf;
+        
+        public Client(Ensure step) {
+            m_step = step;
+        }
+
+        public void updated(Dictionary conf) throws ConfigurationException {
+            m_conf = conf;
+            if (conf != null) {
+                Assert.assertEquals("bar", conf.get("foo"));
+                m_step.step(1);
+            } else {
+                m_step.step();
+            }
+        }
+        
+        void add() {
+            m_step.step();
+            m_dependencies ++;
+        }
+        
+        void remove() {
+            m_step.step();
+            m_dependencies --;
+        }
+                
+        void start() {
+            m_step.step((DEPENDENCIES + 1) /* deps + conf */ + 1 /* start */);
+        }
+
+        void stop() {
+            m_step.step((DEPENDENCIES + 1) /* deps + conf */ + 1 /* start */ + 1 /* stop
*/);
+        }
+        
+        void destroy() {
+            m_step.step((DEPENDENCIES + 1) /* deps + conf */ + 1 /* start */ + 1 /* stop
*/  + 1 /* destroy */);
+        }
+        
+        int getDependencies() {
+            return m_dependencies;
+        }
+        
+        Dictionary getConfiguration() {
+            return m_conf;
+        }
+    }
+}

Added: felix/sandbox/pderop/dependencymanager-prototype/dm/test/test/TestBase.java
URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/dm/test/test/TestBase.java?rev=1571864&view=auto
==============================================================================
--- felix/sandbox/pderop/dependencymanager-prototype/dm/test/test/TestBase.java (added)
+++ felix/sandbox/pderop/dependencymanager-prototype/dm/test/test/TestBase.java Tue Feb 25
23:28:08 2014
@@ -0,0 +1,48 @@
+package test;
+
+import static java.lang.System.out;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+ * Base class for all tests.
+ * For now, this class provides logging support.
+ */
+public class TestBase {
+    final int WARN = 1;
+    final int INFO = 2;
+    final int DEBUG = 3;
+    
+    // Set the enabled log level.
+    final int m_level = WARN;
+    
+    void debug(String format, Object ... params) {
+        if (m_level >= DEBUG) {
+            out.println(Thread.currentThread().getName() + " - " + String.format(format,
params));
+        }
+    }
+    
+    void warn(String format, Object ... params) {
+        warn(format, null, params);
+    }
+    
+    void info(String format, Object ... params) {
+        if (m_level >= INFO) {
+            out.println(Thread.currentThread().getName() + " - " + String.format(format,
params));
+        }
+    }
+
+    void warn(String format, Throwable t, Object ... params) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(Thread.currentThread().getName()).append(" - ").append(String.format(format,
params));
+        if (t != null) {
+            StringWriter buffer = new StringWriter();
+            PrintWriter pw = new PrintWriter(buffer);
+            t.printStackTrace(pw);
+            sb.append(System.getProperty("line.separator"));
+            sb.append(buffer.toString());
+        }
+        System.out.println(sb.toString());
+    }
+}



Mime
View raw message