Try to improve multi bundle locking to make sure we make multi bundle operation atomic and so are not interleaved with single bundle operations.

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@719134 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/framework/src/main/java/org/apache/felix/framework/Felix.java b/framework/src/main/java/org/apache/felix/framework/Felix.java
index 4bc6045..e4060e7 100644
--- a/framework/src/main/java/org/apache/felix/framework/Felix.java
+++ b/framework/src/main/java/org/apache/felix/framework/Felix.java
@@ -1951,7 +1951,7 @@
                 {
                     try
                     {
-                        refreshPackages(new Bundle[] { bundle });
+                        _refreshPackages(new FelixBundle[] { bundle });
                     }
                     catch (Exception ex)
                     {
@@ -2203,7 +2203,7 @@
         {
             try
             {
-                refreshPackages(new Bundle[] { bundle });
+                _refreshPackages(new FelixBundle[] { bundle });
             }
             catch (Exception ex)
             {
@@ -3121,10 +3121,7 @@
                 depIdx++)
             {
                 Bundle b = getBundle(Util.getBundleIdFromModuleId(dependents[depIdx].getId()));
-                if (b != null)
-                {
-                    list.add(b);
-                }
+                list.add(b);
             }
         }
 
@@ -3216,8 +3213,20 @@
 
     protected void refreshPackages(Bundle[] targets)
     {
-        // Acquire locks for all impacted bundles.
         FelixBundle[] bundles = acquireBundleRefreshLocks(targets);
+        try
+        {
+            _refreshPackages(bundles);
+        }
+        finally
+        {
+            // Always release all bundle locks.
+            releaseBundleLocks(bundles);
+        }
+    }
+    
+    protected void _refreshPackages(FelixBundle[] bundles)
+    {
         boolean restart = false;
 
         Bundle systemBundle = getBundle(0);
@@ -3258,51 +3267,42 @@
         {
             forgetUninstalledBundle(bundles[i]);
         }
-
-        try
+        // If there are targets, then refresh each one.
+        if (bundles != null)
         {
-            // If there are targets, then refresh each one.
-            if (bundles != null)
+            // At this point the map contains every bundle that has been
+            // updated and/or removed as well as all bundles that import
+            // packages from these bundles.
+
+            // Create refresh helpers for each bundle.
+            RefreshHelper[] helpers = new RefreshHelper[bundles.length];
+            for (int i = 0; i < bundles.length; i++)
             {
-                // At this point the map contains every bundle that has been
-                // updated and/or removed as well as all bundles that import
-                // packages from these bundles.
-
-                // Create refresh helpers for each bundle.
-                RefreshHelper[] helpers = new RefreshHelper[bundles.length];
-                for (int i = 0; i < bundles.length; i++)
+                if (!bundles[i].getInfo().isExtension())
                 {
-                    if (!bundles[i].getInfo().isExtension())
-                    {
-                        helpers[i] = new RefreshHelper(bundles[i]);
-                    }
-                }
-
-                // Stop, purge or remove, and reinitialize all bundles first.
-                for (int i = 0; i < helpers.length; i++)
-                {
-                    if (helpers[i] != null)
-                    {
-                        helpers[i].stop();
-                        helpers[i].purgeOrRemove();
-                        helpers[i].reinitialize();
-                    }
-                }
-
-                // Then restart all bundles that were previously running.
-                for (int i = 0; i < helpers.length; i++)
-                {
-                    if (helpers[i] != null)
-                    {
-                        helpers[i].restart();
-                    }
+                    helpers[i] = new RefreshHelper(bundles[i]);
                 }
             }
-        }
-        finally
-        {
-            // Always release all bundle locks.
-            releaseBundleLocks(bundles);
+
+            // Stop, purge or remove, and reinitialize all bundles first.
+            for (int i = 0; i < helpers.length; i++)
+            {
+                if (helpers[i] != null)
+                {
+                    helpers[i].stop();
+                    helpers[i].purgeOrRemove();
+                    helpers[i].reinitialize();
+                }
+            }
+
+            // Then restart all bundles that were previously running.
+            for (int i = 0; i < helpers.length; i++)
+            {
+                if (helpers[i] != null)
+                {
+                    helpers[i].restart();
+                }
+            }
         }
 
         fireFrameworkEvent(FrameworkEvent.PACKAGES_REFRESHED, this, null);
@@ -4148,11 +4148,27 @@
             m_installRequestLock_Priority1.notifyAll();
         }
     }
+    
+    private long m_lockCount = 0;
+    private Thread m_lockThread = null;
 
     protected void acquireBundleLock(FelixBundle bundle)
     {
         synchronized (m_bundleLock)
         {
+            while ((m_lockCount < 0) && (m_lockThread != Thread.currentThread()))
+            {
+                try
+                {
+                    m_bundleLock.wait();
+                }
+                catch (InterruptedException e)
+                {
+                    // Ignore and just keep waiting.
+                }
+            }
+            m_lockCount++;
+
             while (!bundle.getInfo().isLockable())
             {
                 try
@@ -4172,6 +4188,18 @@
     {
         synchronized (m_bundleLock)
         {
+            while ((m_lockCount < 0) && (m_lockThread != Thread.currentThread()))
+            {
+                try
+                {
+                    m_bundleLock.wait();
+                }
+                catch (InterruptedException e)
+                {
+                    // Ignore and just keep waiting.
+                }
+            }
+            m_lockCount++;
             if (!bundle.getInfo().isLockable())
             {
                 return false;
@@ -4185,6 +4213,7 @@
     {
         synchronized (m_bundleLock)
         {
+            m_lockCount--;
             bundle.getInfo().unlock();
             m_bundleLock.notifyAll();
         }
@@ -4206,6 +4235,20 @@
 
         synchronized (m_bundleLock)
         {
+            while (m_lockCount != 0)
+            {
+                try
+                {
+                    m_bundleLock.wait();
+                }
+                catch (InterruptedException e)
+                {
+                    // Ignore
+                }
+            }
+            m_lockCount = -1;
+            m_lockThread = Thread.currentThread();
+            
             boolean success = false;
             while (!success)
             {
@@ -4285,6 +4328,20 @@
 
         synchronized (m_bundleLock)
         {
+            while (m_lockCount != 0)
+            {
+                try
+                {
+                    m_bundleLock.wait();
+                }
+                catch (InterruptedException e)
+                {
+                    // Ignore
+                }
+            }
+            m_lockCount = -1;
+            m_lockThread = Thread.currentThread();
+            
             boolean success = false;
             while (!success)
             {
@@ -4396,6 +4453,8 @@
         // Always unlock any locked bundles.
         synchronized (m_bundleLock)
         {
+            m_lockCount = 0;
+            m_lockThread = null;
             for (int i = 0; (bundles != null) && (i < bundles.length); i++)
             {
                 bundles[i].getInfo().unlock();