You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by ri...@apache.org on 2008/12/15 23:50:59 UTC

svn commit: r726867 - /felix/trunk/framework/src/main/java/org/apache/felix/framework/Felix.java

Author: rickhall
Date: Mon Dec 15 14:50:59 2008
New Revision: 726867

URL: http://svn.apache.org/viewvc?rev=726867&view=rev
Log:
Fixed/improved the global locking code for refreshes and resolves; now
bundle locks can be promoted to global locks.

Modified:
    felix/trunk/framework/src/main/java/org/apache/felix/framework/Felix.java

Modified: felix/trunk/framework/src/main/java/org/apache/felix/framework/Felix.java
URL: http://svn.apache.org/viewvc/felix/trunk/framework/src/main/java/org/apache/felix/framework/Felix.java?rev=726867&r1=726866&r2=726867&view=diff
==============================================================================
--- felix/trunk/framework/src/main/java/org/apache/felix/framework/Felix.java (original)
+++ felix/trunk/framework/src/main/java/org/apache/felix/framework/Felix.java Mon Dec 15 14:50:59 2008
@@ -38,7 +38,7 @@
 public class Felix extends FelixBundle implements Framework
 {
     // The secure action used to do privileged calls
-    static SecureAction m_secureAction = new SecureAction();
+    static final SecureAction m_secureAction = new SecureAction();
 
     // The extension manager to handle extension bundles
     ExtensionManager m_extensionManager;
@@ -54,17 +54,24 @@
     private IModuleFactory m_factory = null;
     private R4SearchPolicyCore m_policyCore = null;
 
-    // Object used as a lock when calculating which bundles
-    // when performing an operation on one or more bundles.
-    private Object[] m_bundleLock = new Object[0];
+    // Lock object used to determine if an individual bundle
+    // lock or the global lock can be acquired.
+    private final Object[] m_bundleLock = new Object[0];
+    // Maps a thread object to a single-element int array to
+    // keep track of how many locks the thread has acquired.
+    private final Map m_lockingThreadMap = new HashMap();
+    // Separately keeps track of how many times the global
+    // lock was acquired by a given thread; if this value is
+    // zero, then it means the global lock is free.
+    private int m_globalLockCount = 0;
 
     // Maps a bundle location to a bundle location;
     // used to reserve a location when installing a bundle.
-    private Map m_installRequestMap = new HashMap();
+    private final Map m_installRequestMap = new HashMap();
     // This lock must be acquired to modify m_installRequestMap;
     // to help avoid deadlock this lock as priority 1 and should
     // be acquired before locks with lower priority.
-    private Object[] m_installRequestLock_Priority1 = new Object[0];
+    private final Object[] m_installRequestLock_Priority1 = new Object[0];
 
     // Maps a bundle location to a bundle.
     private HashMap m_installedBundleMap;
@@ -72,14 +79,14 @@
     // This lock must be acquired to modify m_installedBundleMap;
     // to help avoid deadlock this lock as priority 2 and should
     // be acquired before locks with lower priority.
-    private Object[] m_installedBundleLock_Priority2 = new Object[0];
+    private final Object[] m_installedBundleLock_Priority2 = new Object[0];
 
     // An array of uninstalled bundles before a refresh occurs.
     private FelixBundle[] m_uninstalledBundles = null;
     // This lock must be acquired to modify m_uninstalledBundles;
     // to help avoid deadlock this lock as priority 3 and should
     // be acquired before locks with lower priority.
-    private Object[] m_uninstalledBundlesLock_Priority3 = new Object[0];
+    private final Object[] m_uninstalledBundlesLock_Priority3 = new Object[0];
 
     // Framework's active start level.
     private int m_activeStartLevel =
@@ -96,7 +103,7 @@
 
     // Next available bundle identifier.
     private long m_nextId = 1L;
-    private Object m_nextIdLock = new Object[0];
+    private final Object m_nextIdLock = new Object[0];
 
     // List of event listeners.
     private EventDispatcher m_dispatcher = null;
@@ -4177,14 +4184,14 @@
         }
     }
 
-    private long m_lockCount = 0;
-    private Thread m_lockThread = null;
-
-    protected void acquireBundleLock(FelixBundle bundle)
+    void acquireBundleLock(FelixBundle bundle)
     {
         synchronized (m_bundleLock)
         {
-            while ((m_lockCount < 0) && (m_lockThread != Thread.currentThread()))
+            // Wait if any thread has the global lock, unless the current thread
+            // holds the global lock.
+            while ((m_globalLockCount > 0)
+                && m_lockingThreadMap.containsKey(Thread.currentThread()))
             {
                 try
                 {
@@ -4195,8 +4202,17 @@
                     // Ignore and just keep waiting.
                 }
             }
-            m_lockCount++;
 
+            // Increment the current thread's lock count.
+            int[] counter = (int[]) m_lockingThreadMap.get(Thread.currentThread());
+            if (counter == null)
+            {
+                counter = new int[] { 0 };
+            }
+            counter[0]++;
+            m_lockingThreadMap.put(Thread.currentThread(), counter);
+
+            // Wait until the bundle lock is available, then lock it.
             while (!bundle.getInfo().isLockable())
             {
                 try
@@ -4212,11 +4228,14 @@
         }
     }
 
-    protected boolean acquireBundleLockOrFail(FelixBundle bundle)
+    private boolean acquireBundleLockOrFail(FelixBundle bundle)
     {
         synchronized (m_bundleLock)
         {
-            while ((m_lockCount < 0) && (m_lockThread != Thread.currentThread()))
+            // Wait if any thread has the global lock, unless the current thread
+            // holds the global lock.
+            while ((m_globalLockCount > 0)
+                && m_lockingThreadMap.containsKey(Thread.currentThread()))
             {
                 try
                 {
@@ -4227,27 +4246,58 @@
                     // Ignore and just keep waiting.
                 }
             }
-            m_lockCount++;
+
+            // Return immediately if the bundle lock is not available.
             if (!bundle.getInfo().isLockable())
             {
                 return false;
             }
+
+            // Increment the current thread's lock count.
+            int[] counter = (int[]) m_lockingThreadMap.get(Thread.currentThread());
+            if (counter == null)
+            {
+                counter = new int[] { 0 };
+            }
+            counter[0]++;
+            m_lockingThreadMap.put(Thread.currentThread(), counter);
+
+            // Acquire the bundle lock.
             bundle.getInfo().lock();
             return true;
         }
     }
 
-    protected void releaseBundleLock(FelixBundle bundle)
+    void releaseBundleLock(FelixBundle bundle)
     {
         synchronized (m_bundleLock)
         {
-            m_lockCount--;
+            // Decrement the current thread's lock count.
+            int[] counter = (int[]) m_lockingThreadMap.get(Thread.currentThread());
+            if (counter != null)
+            {
+                counter[0]--;
+                if (counter[0] == 0)
+                {
+                    m_lockingThreadMap.remove(Thread.currentThread());
+                }
+                else
+                {
+                    m_lockingThreadMap.put(Thread.currentThread(), counter);
+                }
+            }
+            else
+            {
+                m_logger.log(Logger.LOG_ERROR, "Thread released a lock it doesn't own: " + bundle);
+            }
+
+            // Unlock the bundle.
             bundle.getInfo().unlock();
             m_bundleLock.notifyAll();
         }
     }
 
-    protected FelixBundle[] acquireBundleResolveLocks(Bundle[] targets)
+    private FelixBundle[] acquireBundleResolveLocks(Bundle[] targets)
     {
         // Hold bundles to be locked.
         FelixBundle[] bundles = null;
@@ -4263,7 +4313,12 @@
 
         synchronized (m_bundleLock)
         {
-            while (m_lockCount != 0)
+            // Wait as long as there are multiple threads holding locks,
+            // but proceed if there are no lock holders or the current thread
+            // is the only lock holder.
+            while ((m_lockingThreadMap.size() > 1)
+               || ((m_lockingThreadMap.size() == 1)
+                   && !m_lockingThreadMap.containsKey(Thread.currentThread())))
             {
                 try
                 {
@@ -4274,89 +4329,69 @@
                     // Ignore
                 }
             }
-            m_lockCount = -1;
-            m_lockThread = Thread.currentThread();
 
-            boolean success = false;
-            while (!success)
+            // Increment the current thread's lock count.
+            int[] counter = (int[]) m_lockingThreadMap.get(Thread.currentThread());
+            if (counter == null)
             {
-                // If targets is null, then resolve all unresolved bundles.
-                if (targets == null)
-                {
-                    List list = new ArrayList();
+                counter = new int[] { 0 };
+            }
+            counter[0]++;
+            m_lockingThreadMap.put(Thread.currentThread(), counter);
 
-                    // Add all unresolved bundles to the list.
-                    synchronized (m_installedBundleLock_Priority2)
-                    {
-                        Iterator iter = m_installedBundleMap.values().iterator();
-                        while (iter.hasNext())
-                        {
-                            FelixBundle bundle = (FelixBundle) iter.next();
-                            if (bundle.getInfo().getState() == Bundle.INSTALLED)
-                            {
-                                list.add(bundle);
-                            }
-                        }
-                    }
+            // Increment the current thread's global lock count.
+            m_globalLockCount++;
 
-                    // Create an array.
-                    if (list.size() > 0)
-                    {
-                        bundles = (FelixBundle[]) list.toArray(new FelixBundle[list.size()]);
-                    }
-                }
+            // If targets is null, then resolve all unresolved bundles.
+            if (targets == null)
+            {
+                List list = new ArrayList();
 
-                // Check if all unresolved bundles can be locked.
-                boolean lockable = true;
-                if (bundles != null)
+                // Add all unresolved bundles to the list.
+                synchronized (m_installedBundleLock_Priority2)
                 {
-                    for (int i = 0; lockable && (i < bundles.length); i++)
-                    {
-                        lockable = bundles[i].getInfo().isLockable();
-                    }
-
-                    // If we can lock all bundles, then lock them.
-                    if (lockable)
-                    {
-                        for (int i = 0; i < bundles.length; i++)
-                        {
-                            bundles[i].getInfo().lock();
-                        }
-                        success = true;
-                    }
-                    // Otherwise, wait and try again.
-                    else
+                    Iterator iter = m_installedBundleMap.values().iterator();
+                    while (iter.hasNext())
                     {
-                        try
-                        {
-                            m_bundleLock.wait();
-                        }
-                        catch (InterruptedException ex)
+                        FelixBundle bundle = (FelixBundle) iter.next();
+                        if (bundle.getInfo().getState() == Bundle.INSTALLED)
                         {
-                            // Ignore and just keep waiting.
+                            list.add(bundle);
                         }
                     }
                 }
-                else
+
+                // Create an array.
+                if (list.size() > 0)
                 {
-                    // If there were no bundles to lock, then we can just
-                    // exit the lock loop.
-                    success = true;
+                    bundles = (FelixBundle[]) list.toArray(new FelixBundle[list.size()]);
                 }
             }
+
+            // Lock all needed bundles; this is not strictly
+            // necessary since we hold the global lock.
+            for (int i = 0; i < bundles.length; i++)
+            {
+                bundles[i].getInfo().lock();
+            }
         }
 
         return bundles;
     }
 
-    protected FelixBundle[] acquireBundleRefreshLocks(Bundle[] targets)
+    private FelixBundle[] acquireBundleRefreshLocks(Bundle[] targets)
     {
         // Hold bundles to be locked.
         FelixBundle[] bundles = null;
 
         synchronized (m_bundleLock)
         {
-            while (m_lockCount != 0)
+            // Wait as long as there are multiple threads holding locks,
+            // but proceed if there are no lock holders or the current thread
+            // is the only lock holder.
+            while ((m_lockingThreadMap.size() > 1)
+               || ((m_lockingThreadMap.size() == 1)
+                   && !m_lockingThreadMap.containsKey(Thread.currentThread())))
             {
                 try
                 {
@@ -4367,122 +4402,119 @@
                     // Ignore
                 }
             }
-            m_lockCount = -1;
-            m_lockThread = Thread.currentThread();
 
-            boolean success = false;
-            while (!success)
+            // Increment the current thread's lock count.
+            int[] counter = (int[]) m_lockingThreadMap.get(Thread.currentThread());
+            if (counter == null)
             {
-                // If targets is null, then refresh all pending bundles.
-                Bundle[] newTargets = targets;
-                if (newTargets == null)
-                {
-                    List list = new ArrayList();
-
-                    // First add all uninstalled bundles.
-                    synchronized (m_uninstalledBundlesLock_Priority3)
-                    {
-                        for (int i = 0;
-                            (m_uninstalledBundles != null) && (i < m_uninstalledBundles.length);
-                            i++)
-                        {
-                            list.add(m_uninstalledBundles[i]);
-                        }
-                    }
+                counter = new int[] { 0 };
+            }
+            counter[0]++;
+            m_lockingThreadMap.put(Thread.currentThread(), counter);
 
-                    // Then add all updated bundles.
-                    synchronized (m_installedBundleLock_Priority2)
-                    {
-                        Iterator iter = m_installedBundleMap.values().iterator();
-                        while (iter.hasNext())
-                        {
-                            FelixBundle bundle = (FelixBundle) iter.next();
-                            BundleInfo info = bundle.getInfo();
-                            if ((info instanceof RegularBundleInfo) &&
-                                (((RegularBundleInfo) info).getArchive().getRevisionCount() > 1))
-                            {
-                                list.add(bundle);
-                            }
-                        }
-                    }
+            // Increment the current thread's global lock count.
+            m_globalLockCount++;
 
-                    // Create an array.
-                    if (list.size() > 0)
-                    {
-                        newTargets = (Bundle[]) list.toArray(new Bundle[list.size()]);
-                    }
-                }
+            // If targets is null, then refresh all pending bundles.
+            Bundle[] newTargets = targets;
+            if (newTargets == null)
+            {
+                List list = new ArrayList();
 
-                // If there are targets, then find all dependencies
-                // for each one.
-                if (newTargets != null)
+                // First add all uninstalled bundles.
+                synchronized (m_uninstalledBundlesLock_Priority3)
                 {
-                    // Create map of bundles that import the packages
-                    // from the target bundles.
-                    Map map = new HashMap();
-                    for (int targetIdx = 0; targetIdx < newTargets.length; targetIdx++)
+                    for (int i = 0;
+                        (m_uninstalledBundles != null) && (i < m_uninstalledBundles.length);
+                        i++)
                     {
-                        // Add the current target bundle to the map of
-                        // bundles to be refreshed.
-                        FelixBundle target = (FelixBundle) newTargets[targetIdx];
-                        map.put(target, target);
-                        // Add all importing bundles to map.
-                        populateDependentGraph(target, map);
+                        list.add(m_uninstalledBundles[i]);
                     }
-
-                    bundles = (FelixBundle[]) map.values().toArray(new FelixBundle[map.size()]);
                 }
 
-                // Check if all corresponding bundles can be locked.
-                boolean lockable = true;
-                if (bundles != null)
+                // Then add all updated bundles.
+                synchronized (m_installedBundleLock_Priority2)
                 {
-                    for (int i = 0; lockable && (i < bundles.length); i++)
+                    Iterator iter = m_installedBundleMap.values().iterator();
+                    while (iter.hasNext())
                     {
-                        lockable = bundles[i].getInfo().isLockable();
-                    }
-
-                    // If we can lock all bundles, then lock them.
-                    if (lockable)
-                    {
-                        for (int i = 0; i < bundles.length; i++)
+                        FelixBundle bundle = (FelixBundle) iter.next();
+                        BundleInfo info = bundle.getInfo();
+                        if ((info instanceof RegularBundleInfo) &&
+                            (((RegularBundleInfo) info).getArchive().getRevisionCount() > 1))
                         {
-                            bundles[i].getInfo().lock();
-                        }
-                        success = true;
-                    }
-                    // Otherwise, wait and try again.
-                    else
-                    {
-                        try
-                        {
-                            m_bundleLock.wait();
-                        }
-                        catch (InterruptedException ex)
-                        {
-                            // Ignore and just keep waiting.
+                            list.add(bundle);
                         }
                     }
                 }
-                else
+
+                // Create an array.
+                if (list.size() > 0)
                 {
-                    // If there were no bundles to lock, then we can just
-                    // exit the lock loop.
-                    success = true;
+                    newTargets = (Bundle[]) list.toArray(new Bundle[list.size()]);
                 }
             }
+
+            // If there are targets, then find all dependencies
+            // for each one.
+            if (newTargets != null)
+            {
+                // Create map of bundles that import the packages
+                // from the target bundles.
+                Map map = new HashMap();
+                for (int targetIdx = 0; targetIdx < newTargets.length; targetIdx++)
+                {
+                    // Add the current target bundle to the map of
+                    // bundles to be refreshed.
+                    FelixBundle target = (FelixBundle) newTargets[targetIdx];
+                    map.put(target, target);
+                    // Add all importing bundles to map.
+                    populateDependentGraph(target, map);
+                }
+
+                bundles = (FelixBundle[]) map.values().toArray(new FelixBundle[map.size()]);
+            }
+
+            // Lock all needed bundles; this is not strictly
+            // necessary since we hold the global lock.
+            for (int i = 0; i < bundles.length; i++)
+            {
+                bundles[i].getInfo().lock();
+            }
         }
 
         return bundles;
     }
 
-    protected void releaseBundleLocks(FelixBundle[] bundles)
+    private void releaseBundleLocks(FelixBundle[] bundles)
     {
         // Always unlock any locked bundles.
         synchronized (m_bundleLock)
         {
-            m_lockCount = 0;
-            m_lockThread = null;
+            // Decrement the current thread's lock count.
+            int[] counter = (int[]) m_lockingThreadMap.get(Thread.currentThread());
+            if (counter != null)
+            {
+                counter[0]--;
+                if (counter[0] == 0)
+                {
+                    m_lockingThreadMap.remove(Thread.currentThread());
+                }
+                else
+                {
+                    m_lockingThreadMap.put(Thread.currentThread(), counter);
+                }
+                counter = new int[] { 0 };
+            }
+            else
+            {
+                m_logger.log(Logger.LOG_ERROR, "Thread release a lock it doesn't own.");
+            }
+
+            // Decrement the current thread's global lock count;
+            m_globalLockCount--;
+
+            // Unlock all the bundles.
             for (int i = 0; (bundles != null) && (i < bundles.length); i++)
             {
                 bundles[i].getInfo().unlock();