Author: chetanm
Date: Mon Jun 29 06:03:35 2015
New Revision: 1688089

URL: http://svn.apache.org/r1688089
Log:
OAK-2401 - SegmentNodeStoreService prone to deadlocks

Adding testcase

Added:
    
jackrabbit/oak/trunk/oak-pojosr/src/test/groovy/org/apache/jackrabbit/oak/run/osgi/SegmentNodeStoreConfigTest.groovy

Added: 
jackrabbit/oak/trunk/oak-pojosr/src/test/groovy/org/apache/jackrabbit/oak/run/osgi/SegmentNodeStoreConfigTest.groovy
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-pojosr/src/test/groovy/org/apache/jackrabbit/oak/run/osgi/SegmentNodeStoreConfigTest.groovy?rev=1688089&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-pojosr/src/test/groovy/org/apache/jackrabbit/oak/run/osgi/SegmentNodeStoreConfigTest.groovy
 (added)
+++ 
jackrabbit/oak/trunk/oak-pojosr/src/test/groovy/org/apache/jackrabbit/oak/run/osgi/SegmentNodeStoreConfigTest.groovy
 Mon Jun 29 06:03:35 2015
@@ -0,0 +1,157 @@
+/*
+ * 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.jackrabbit.oak.run.osgi
+
+import com.google.common.collect.Lists
+import com.google.common.collect.Maps
+import org.apache.felix.connect.launch.PojoServiceRegistry
+import org.apache.jackrabbit.oak.spi.state.NodeState
+import org.apache.jackrabbit.oak.spi.state.NodeStore
+import org.junit.Test
+import org.osgi.framework.BundleContext
+import org.osgi.framework.ServiceReference
+import org.osgi.util.tracker.ServiceTracker
+
+import java.lang.management.ManagementFactory
+import java.util.concurrent.CountDownLatch
+
+import static org.junit.Assert.assertNull
+
+class SegmentNodeStoreConfigTest extends AbstractRepositoryFactoryTest {
+
+    private PojoServiceRegistry registry
+
+    @Test
+    public void testDeadlock() throws Exception {
+        registry = repositoryFactory.initializeServiceRegistry(config)
+
+
+        CountDownLatch deactivateLatch = new CountDownLatch(1)
+        CountDownLatch trackerLatch = new CountDownLatch(1)
+        CountDownLatch mainLatch = new CountDownLatch(1)
+        NodeStoreTracker tracker = new 
NodeStoreTracker(registry.getBundleContext(), trackerLatch, deactivateLatch, 
mainLatch)
+
+        //1. Get NodeStore created
+        createConfig([
+                
'org.apache.jackrabbit.oak.plugins.segment.SegmentNodeStoreService': [
+                        cache: 256
+                ]
+        ])
+        getServiceWithWait(NodeStore.class)
+        assert tracker.stores
+
+        CountDownLatch allWellLatch = new CountDownLatch(1)
+        //4. Get roots and thus wait on latch holding the lock
+        Thread.start("GetRoots") { tracker.getRoots(); 
allWellLatch.countDown() }
+
+        //3. Mutate the config. This results in NodeStore deactivate
+        //which first wait for nodeStoreLatch and then on NodeStoreTracker lock
+        createConfig([
+                
'org.apache.jackrabbit.oak.plugins.segment.SegmentNodeStoreService': [
+                        cache: 200
+                ]
+        ])
+
+        //4. Wait for the "GetRoots" to indicate that it has acquired 
"trackerLock"
+        mainLatch.await()
+
+        assertNull("Deadlock detected", 
ManagementFactory.getThreadMXBean().findDeadlockedThreads())
+        //5. Let NodeStore deactivate thread proceed and wait for "trackerLock"
+        deactivateLatch.countDown()
+        println ("Letting the deactivate call proceed")
+
+        //6. Let "GetRoots" thread proceed and make it fetch Root and thus 
wait for
+        //lock in SegmentNodeStoreService
+        trackerLatch.countDown()
+        println ("Letting the getRoots call proceed")
+
+        assertNull("Deadlock detected", 
ManagementFactory.getThreadMXBean().findDeadlockedThreads())
+        allWellLatch.await()
+        tracker.close()
+    }
+
+    @Override
+    protected PojoServiceRegistry getRegistry() {
+        return registry
+    }
+
+    private static class NodeStoreTracker extends ServiceTracker<NodeStore, 
NodeStore> {
+        private final Map<ServiceReference<NodeStore>, NodeStore> stores = 
Maps.newHashMap();
+        private final Object trackerLock = new Object();
+        private final CountDownLatch trackerLatch;
+        private final CountDownLatch deactivateLatch;
+        private final CountDownLatch mainLatch;
+
+        NodeStoreTracker(BundleContext context,
+                         CountDownLatch trackerLatch,
+                         CountDownLatch deactivateLatch,
+                         CountDownLatch mainLatch) {
+            super(context, NodeStore.class.name, null)
+            this.trackerLatch = trackerLatch
+            this.deactivateLatch = deactivateLatch
+            this.mainLatch = mainLatch
+            super.open();
+        }
+
+        @Override
+        void removedService(ServiceReference<NodeStore> reference, NodeStore 
service) {
+            deactivateLatch.await()
+            synchronized (trackerLock) {
+                stores.remove(reference)
+            }
+            super.removedService(reference, service)
+        }
+
+        @Override
+        NodeStore addingService(ServiceReference<NodeStore> reference) {
+            NodeStore store = super.addingService(reference)
+            synchronized (trackerLock) {
+                stores.put(reference, store)
+            }
+            return store;
+        }
+
+        public List<NodeState> getRoots() {
+            List<NodeState> result = Lists.newArrayList();
+            synchronized (trackerLock) {
+                mainLatch.countDown()
+                trackerLatch.await()
+
+                for (NodeStore store : stores.values()) {
+                    try {
+                        result.add(store.getRoot());
+                    } catch (Exception e) {
+                        //Exception expected
+                        e.printStackTrace()
+                    }
+                }
+            }
+            return result;
+        }
+
+        public List<NodeStore> getStores() {
+            List<NodeStore> result = Lists.newArrayList();
+            synchronized (trackerLock) {
+                result.addAll(stores.values())
+            }
+            return result;
+        }
+    }
+}


Reply via email to