db-derby-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From kahat...@apache.org
Subject svn commit: r535851 - in /db/derby/code/trunk/java/engine: ./ org/apache/derby/ org/apache/derby/impl/services/ org/apache/derby/impl/services/locks/
Date Mon, 07 May 2007 11:28:43 GMT
Author: kahatlen
Date: Mon May  7 04:28:40 2007
New Revision: 535851

URL: http://svn.apache.org/viewvc?view=rev&rev=535851
Log:
DERBY-2327: Reduce monitor contention in LockSet

Added a new LockSet implementation which uses the classes in
java.util.concurrent to achieve more concurrency.

Added:
    db/derby/code/trunk/java/engine/org/apache/derby/impl/services/locks/ConcurrentLockSet.java
      - copied, changed from r520345, db/derby/code/trunk/java/engine/org/apache/derby/impl/services/locks/LockSet.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/services/locks/ConcurrentPool.java
  (with props)
Modified:
    db/derby/code/trunk/java/engine/build.xml
    db/derby/code/trunk/java/engine/org/apache/derby/impl/services/build.xml
    db/derby/code/trunk/java/engine/org/apache/derby/impl/services/locks/Deadlock.java
    db/derby/code/trunk/java/engine/org/apache/derby/modules.properties

Modified: db/derby/code/trunk/java/engine/build.xml
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/build.xml?view=diff&rev=535851&r1=535850&r2=535851
==============================================================================
--- db/derby/code/trunk/java/engine/build.xml (original)
+++ db/derby/code/trunk/java/engine/build.xml Mon May  7 04:28:40 2007
@@ -50,7 +50,7 @@
 
 <!--             ============ Begin Targets ==============                -->
  
-  <target name="engine" depends="engine_j2se,engine_169,engine_169_opt,jdbc4_modules_edit"
+  <target name="engine" depends="engine_j2se,engine_169,engine_169_opt,jdbc4_modules_edit,jdk15_modules_edit"
           description="Build Derby engine">
 	</target>
     <target name="engine_j2se" depends="engine_169"
@@ -118,6 +118,14 @@
                               byline="true" />
         </target>
 
+        <!-- use jdk16 to compile jdk15 code -->
+        <target name="jdk15_modules_edit"
+                if="jdk16">
+          <replaceregexp
+             file="${out.dir}/${derby.dir}/modules.properties"
+             match="^#jdk15_optional_(.*)"
+             replace="\1" byline="true"/>
+        </target>
 	
 <!--             ============= End Targets ==============                -->
 

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/services/build.xml
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/services/build.xml?view=diff&rev=535851&r1=535850&r2=535851
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/services/build.xml (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/services/build.xml Mon May  7 04:28:40
2007
@@ -53,10 +53,37 @@
       </classpath>
       <include name="${derby.dir}/impl/services/**"/>
       <exclude name="${derby.dir}/impl/services/jce/**"/>
+      <exclude name="${derby.dir}/impl/services/locks/Concurrent*.java"/>
     </javac>
  </target>
  
- <target name="compile_impl_services" depends="compile_impl_services_169">
+ <!-- use jdk16 to compile jdk15 code -->
+ <target name="compile_impl_services_jdk15" if="jdk16"
+         depends="compile_impl_services_169">
+   <javac
+      source="1.5"
+      target="1.5"
+      fork="yes"
+      executable="${jdk16}/bin/javac"
+      bootclasspath="${empty}"
+      nowarn="on"
+      debug="${debug}"
+      depend="${depend}"
+      deprecation="${deprecation}"
+      optimize="${optimize}"
+      proceed="${proceed}"
+      verbose="${verbose}"
+      srcdir="${derby.engine.src.dir};${derby.engine.src.dir}"
+      destdir="${out.dir}">
+     <classpath>
+       <pathelement path="${java16compile.classpath}"/>
+     </classpath>
+     <include name="${derby.dir}/impl/services/locks/Concurrent*.java"/>
+   </javac>
+ </target>
+
+ <target name="compile_impl_services"
+         depends="compile_impl_services_jdk15,compile_impl_services_169">
     <javac
       source="1.4"
       target="1.4"

Copied: db/derby/code/trunk/java/engine/org/apache/derby/impl/services/locks/ConcurrentLockSet.java
(from r520345, db/derby/code/trunk/java/engine/org/apache/derby/impl/services/locks/LockSet.java)
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/services/locks/ConcurrentLockSet.java?view=diff&rev=535851&p1=db/derby/code/trunk/java/engine/org/apache/derby/impl/services/locks/LockSet.java&r1=520345&p2=db/derby/code/trunk/java/engine/org/apache/derby/impl/services/locks/ConcurrentLockSet.java&r2=535851
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/services/locks/LockSet.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/services/locks/ConcurrentLockSet.java
Mon May  7 04:28:40 2007
@@ -1,6 +1,6 @@
 /*
 
-   Derby - Class org.apache.derby.impl.services.locks.LockSet
+   Derby - Class org.apache.derby.impl.services.locks.ConcurrentLockSet
 
    Licensed to the Apache Software Foundation (ASF) under one or more
    contributor license agreements.  See the NOTICE file distributed with
@@ -34,49 +34,63 @@
 import org.apache.derby.iapi.reference.Property;
 import org.apache.derby.iapi.reference.SQLState;
 
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Enumeration;
-import java.util.Iterator;
 import java.util.Map;
 
 
 /**
-	A LockSet is a complete lock table.	A lock table is a hash table
-	keyed by a Lockable and with a LockControl as the data element.
+    A ConcurrentLockSet is a complete lock table which maps
+    <code>Lockable</code>s to <code>LockControl</code> objects.
 
 	<P>
 	A LockControl contains information about the locks held on a Lockable.
 
 	<BR>
-	MT - Mutable - Container Object : All non-private methods of this class are
-	thread safe unless otherwise stated by their javadoc comments.
+    MT - Mutable : All public methods of this class, except addWaiters, are
+    thread safe. addWaiters can only be called from the thread which performs
+    deadlock detection. Only one thread can perform deadlock detection at a
+    time.
 
 	<BR>
-	All searching of
-    the hashtable is performed using java synchroization(this).
-	<BR>
 	The class creates ActiveLock and LockControl objects.
 	
 	LockControl objects are never passed out of this class, All the methods of 
-    LockControl are called while being synchronized on this, thus providing the
+    LockControl are called while holding a ReentrantLock associated with the
+    Lockable controlled by the LockControl, thus providing the
     single threading that LockControl required.
 
 	Methods of Lockables are only called by this class or LockControl, and 
-    always while being synchronized on this, thus providing the single 
-    threading that Lockable requires.
+    always while holding the corresponding ReentrantLock, thus providing the
+    single threading that Lockable requires.
 	
 	@see LockControl
 */
 
-final class LockSet implements LockTable {
+final class ConcurrentLockSet implements LockTable {
 	/*
 	** Fields
 	*/
-	private final SinglePool factory;
+	private final AbstractPool factory;
 
     /** Hash table which maps <code>Lockable</code> objects to
      * <code>Lock</code>s. */
-    private final HashMap locks;
+    private final ConcurrentHashMap<Lockable, Entry> locks;
+
+    /**
+     * List containing all entries seen by the last call to
+     * <code>addWaiters()</code>. Makes it possible for the deadlock detection
+     * thread to lock all the entries it has visited until it has
+     * finished. This prevents false deadlocks from being reported (because all
+     * observed waiters must still be waiting when the deadlock detection has
+     * completed).
+     */
+    private ArrayList<Entry> seenByDeadlockDetection;
 
 	/**
 		Timeout for deadlocks, in ms.
@@ -95,17 +109,181 @@
 //EXCLUDE-END-lockdiag- 
 
 	// The number of waiters for locks
-	private int blockCount;
+	private final AtomicInteger blockCount;
 
 	/*
 	** Constructor
 	*/
 
-	protected LockSet(SinglePool factory) {
+	ConcurrentLockSet(AbstractPool factory) {
 		this.factory = factory;
-		locks = new HashMap();
+        blockCount = new AtomicInteger();
+		locks = new ConcurrentHashMap<Lockable, Entry>();
 	}
 
+    /**
+     * Class representing an entry in the lock table.
+     */
+    private static final class Entry {
+        /** The lock control. */
+        Control control;
+        /**
+         * Mutex used to ensure single-threaded access to the LockControls. To
+         * avoid Java deadlocks, no thread should ever hold the mutex of more
+         * than one entry. Excepted from this requirement is a thread which
+         * performs deadlock detection. During deadlock detection, a thread
+         * might hold several mutexes, but it is not allowed to hold any mutex
+         * when entering the deadlock detection. Only one thread is allowed to
+         * perform deadlock detection at a time.
+         */
+        private final ReentrantLock mutex = new ReentrantLock();
+        /**
+         * Condition variable which prevents calls to <code>lock()</code> from
+         * locking the entry. If it is not <code>null</code>, only the thread
+         * performing deadlock detection may lock the entry (by calling
+         * <code>lockForDeadlockDetection()</code>).
+         */
+        private Condition deadlockDetection;
+
+        /**
+         * Lock the entry, ensuring exclusive access to the contained
+         * <code>Control</code> object. The call will block until the entry can
+         * be locked. If the entry is unlocked and
+         * <code>deadlockDetection</code> is not <code>null</code>,
the entry
+         * belongs to a thread which waits for deadlock detection to be
+         * initiated, and the call will block until that thread has finished
+         * its deadlock detection.
+         */
+        void lock() {
+            if (SanityManager.DEBUG) {
+                SanityManager.ASSERT(!mutex.isHeldByCurrentThread());
+            }
+            mutex.lock();
+            while (deadlockDetection != null) {
+                deadlockDetection.awaitUninterruptibly();
+            }
+        }
+
+        /**
+         * Unlock the entry, allowing other threads to lock and access the
+         * contained <code>Control</code> object.
+         */
+        void unlock() {
+            mutex.unlock();
+        }
+
+        /**
+         * Lock the entry while performing deadlock detection. This method will
+         * lock the entry even when <code>deadlockDetection</code> is not
+         * <code>null</code>. If <code>deadlockDetection</code> is
not
+         * <code>null</code>, we know the entry and its <code>Control</code>
+         * will not be accessed by others until we have finished the deadlock
+         * detection, so it's OK for us to access it.
+         *
+         */
+        void lockForDeadlockDetection() {
+            if (SanityManager.DEBUG) {
+                SanityManager.ASSERT(!mutex.isHeldByCurrentThread());
+            }
+            mutex.lock();
+        }
+
+        /**
+         * Notify that the lock request that is currently accessing the entry
+         * will be entering deadlock detection. Unlock the entry to allow the
+         * current thread or other threads to lock the entry for deadlock
+         * detection, but set the condition variable to prevent regular locking
+         * of the entry.
+         */
+        void enterDeadlockDetection() {
+            deadlockDetection = mutex.newCondition();
+            mutex.unlock();
+        }
+
+        /**
+         * Notify that the deadlock detection triggered by the current thread
+         * has finished. Re-lock the entry and notify any waiters that the
+         * deadlock detection has completed.
+         */
+        void exitDeadlockDetection() {
+            if (SanityManager.DEBUG) {
+                SanityManager.ASSERT(!mutex.isHeldByCurrentThread());
+            }
+            mutex.lock();
+            deadlockDetection.signalAll();
+            deadlockDetection = null;
+        }
+    }
+
+    /**
+     * Get an entry from the lock table. If no entry exists for the
+     * <code>Lockable</code>, insert an entry. The returned entry will be
+     * locked and is guaranteed to still be present in the table.
+     *
+     * @param ref the <code>Lockable</code> whose entry to return
+     * @return the entry for the <code>Lockable</code>, locked for exclusive
+     * access
+     */
+    private Entry getEntry(Lockable ref) {
+        Entry e = locks.get(ref);
+        while (true) {
+            if (e != null) {
+                e.lock();
+                if (e.control != null) {
+                    // entry is found and in use, return it
+                    return e;
+                }
+                // entry is empty, hence it was removed from the table after we
+                // retrieved it. Try to reuse it later.
+            } else {
+                // no entry found, create a new one
+                e = new Entry();
+                e.lock();
+            }
+            // reinsert empty entry, or insert the new entry
+            Entry current = locks.putIfAbsent(ref, e);
+            if (current == null) {
+                // successfully (re-)inserted entry, return it
+                return e;
+            }
+            // someone beat us, unlock the old entry and retry with the entry
+            // they inserted
+            e.unlock();
+            e = current;
+        }
+    }
+
+    /**
+     * Check whether there is a deadlock. Make sure that only one thread enters
+     * deadlock detection at a time.
+     *
+     * @param entry the entry in the lock table for the lock request that
+     * triggered deadlock detection
+     * @param waitingLock the waiting lock
+     * @param wakeupReason the reason for waking up the waiter
+     * @return an object describing the deadlock
+     */
+    private Object[] checkDeadlock(Entry entry, ActiveLock waitingLock,
+                                   byte wakeupReason) {
+        LockControl control = (LockControl) entry.control;
+        // make sure that the entry is not blocking other threads performing
+        // deadlock detection since we have to wait for them to finish
+        entry.enterDeadlockDetection();
+        synchronized (Deadlock.class) {
+            try {
+                return Deadlock.look(factory, this, control, waitingLock,
+                                     wakeupReason);
+            } finally {
+                // unlock all entries we visited
+                for (Entry e : seenByDeadlockDetection) {
+                    e.unlock();
+                }
+                seenByDeadlockDetection = null;
+                // re-lock the entry
+                entry.exitDeadlockDetection();
+            }
+        }
+    }
 
 	/*
 	** Public Methods
@@ -138,14 +316,14 @@
 			}
 		}
 
-		Control gc;
 		LockControl control;
 		Lock lockItem;
         String  lockDebug = null;
 
-		synchronized (this) {
+        Entry entry = getEntry(ref);
+        try {
 
-			gc = getControl(ref);
+            Control gc = entry.control;
 
 			if (gc == null) {
 
@@ -154,27 +332,21 @@
 
 				gl.grant();
 
-				locks.put(ref, gl);
+				entry.control = gl;
 
 				return gl;
 			}
 
 			control = gc.getLockControl();
 			if (control != gc) {
-				locks.put(ref, control);
+				entry.control = control;
 			}
-			
 
 			if (SanityManager.DEBUG) {
 				SanityManager.ASSERT(ref.equals(control.getLockable()));
-
 				// ASSERT item is in the list
-                if (getControl(control.getLockable()) != control)
-                {
-					SanityManager.THROWASSERT(
-                        "lockObject mismatched lock items " + 
-                        getControl(control.getLockable()) + " " + control);
-                }
+                SanityManager.ASSERT(
+                    locks.get(control.getLockable()).control == control);
 			}
 
 			lockItem = control.addLock(this, compatibilitySpace, qualifier);
@@ -201,15 +373,30 @@
 
                         lockDebug = 
                             DiagnosticUtil.toDiagString(lockItem)   +
-                            "\nCould not grant lock with zero timeout, here's the table"
+
-                            this.toDebugString();
+                            "\nCould not grant lock with zero timeout, " +
+                            "here's the table";
+
+                        // We cannot hold a lock on an entry while calling
+                        // toDebugString() since it will lock other entries in
+                        // the lock table. Holding the lock could cause a
+                        // deadlock.
+                        entry.unlock();
+                        try {
+                            lockDebug += toDebugString();
+                        } finally {
+                            // Re-lock the entry so that the outer finally
+                            // clause doesn't fail.
+                            entry.lock();
+                        }
                     }
                 }
 
 				return null;
 			}
 
-		} // synchronized block
+        } finally {
+            entry.unlock();
+        }
 
 		boolean deadlockWait = false;
 		int actualTimeout;
@@ -283,7 +470,8 @@
                     Enumeration timeoutLockTable = null;
                     long currentTime = 0;
         
-                    synchronized (this) {
+                    entry.lock();
+                    try {
 
                         if (control.isGrantable(
                                 control.firstWaiter() == waitingLock,
@@ -315,9 +503,7 @@
                             // because we were selected as a victim we still 
                             // check because the situation may have changed.
                             deadlockData = 
-                                Deadlock.look(
-                                    factory, this, control, waitingLock, 
-                                    wakeupReason);
+                                checkDeadlock(entry, waitingLock, wakeupReason);
 
                             if (deadlockData == null) {
                                 // we don't have a deadlock
@@ -338,39 +524,22 @@
 
                         // If we were not woken by another then we have
                         // timed out. Either deadlock out or timeout
-                        if (willQuitWait) {
-
-                            if (SanityManager.DEBUG) 
-                            {
-                                if (SanityManager.DEBUG_ON("DeadlockTrace"))
-                                {
-
-                                    SanityManager.showTrace(new Throwable());
-
-                                    // The following dumps the lock table as it 
-                                    // exists at the time a timeout is about to 
-                                    // cause a deadlock exception to be thrown.
-
-                                    lockDebug = 
-                                    DiagnosticUtil.toDiagString(waitingLock)   +
-                                    "\nGot deadlock/timeout, here's the table" +
-                                    this.toDebugString();
-                                }
-                            }
-                            
-                            if (deadlockTrace && (deadlockData == null))
-                            {
-                                // if ending lock request due to lock timeout
-                                // want a copy of the LockTable and the time,
-                                // in case of deadlock deadlockData has the
-                                // info we need.
-                                currentTime = System.currentTimeMillis(); 
-                                timeoutLockTable = 
-                                    factory.makeVirtualLockTable();
-                            }
+                        if (SanityManager.DEBUG &&
+                                SanityManager.DEBUG_ON("DeadlockTrace") &&
+                                willQuitWait) {
+                            // Generate the first part of the debug message
+                            // while holding the lock on entry, so that we have
+                            // exclusive access to waitingLock. Wait until the
+                            // entry has been unlocked before appending the
+                            // contents of the lock table (to avoid deadlocks).
+                            lockDebug =
+                                DiagnosticUtil.toDiagString(waitingLock) +
+                                "\nGot deadlock/timeout, here's the table";
                         }
 
-                    } // synchronized block
+                    } finally {
+                        entry.unlock();
+                    }
 
                     // need to do this outside of the synchronized block as the
                     // message text building (timeouts and deadlocks) may 
@@ -379,8 +548,28 @@
 
                     if (willQuitWait)
                     {
+                        if (deadlockTrace && (deadlockData == null)) {
+                            // if ending lock request due to lock timeout
+                            // want a copy of the LockTable and the time,
+                            // in case of deadlock deadlockData has the
+                            // info we need.
+                            currentTime = System.currentTimeMillis();
+                            timeoutLockTable =
+                                factory.makeVirtualLockTable();
+                        }
+
                         if (SanityManager.DEBUG)
                         {
+                            if (SanityManager.DEBUG_ON("DeadlockTrace")) {
+                                SanityManager.showTrace(new Throwable());
+
+                                // The following dumps the lock table as it
+                                // exists at the time a timeout is about to
+                                // cause a deadlock exception to be thrown.
+
+                                lockDebug += toDebugString();
+                            }
+
                             if (lockDebug != null)
                             {
                                 String type = 
@@ -465,8 +654,28 @@
 
 	*/
 	public void unlock(Latch item, int unlockCount) {
+        // assume LockEntry is there
+        Entry entry = locks.get(item.getLockable());
+        entry.lock();
+        try {
+            unlock(entry, item, unlockCount);
+        } finally {
+            entry.unlock();
+        }
+    }
 
+    /**
+     * Unlock an object, previously locked by lockObject().
+     *
+     * @param entry the entry in which the lock is contained (the current
+     * thread must have locked the entry)
+     * @param item the item to unlock
+     * @param unlockCount the number of times to unlock the item (if zero, take
+     * the unlock count from item)
+     */
+    private void unlock(Entry entry, Latch item, int unlockCount) {
 		if (SanityManager.DEBUG) {
+            SanityManager.ASSERT(entry.mutex.isHeldByCurrentThread());
 			if (SanityManager.DEBUG_ON(Constants.LOCK_TRACE)) {
 				/*
 				** I don't like checking the trace flag twice, but SanityManager
@@ -482,9 +691,7 @@
 		boolean tryGrant = false;
 		ActiveLock nextGrant = null;
 
-		synchronized (this) {
-
-			Control control = getControl(item.getLockable());
+        Control control = entry.control;
 			
 			if (SanityManager.DEBUG) {
 
@@ -506,12 +713,8 @@
                         "item = " + DiagnosticUtil.toDiagString(item));
                 }
 
-				if (getControl(control.getLockable()) != control)
-                {
-                    SanityManager.THROWASSERT(
-                        "unlock mismatched lock items " + 
-                        getControl(control.getLockable()) + " " + control);
-                }
+                SanityManager.ASSERT(
+                    locks.get(control.getLockable()).control == control);
 
 				if ((unlockCount != 0) && (unlockCount > item.getCount()))
 					SanityManager.THROWASSERT("unlockCount " + unlockCount +
@@ -535,10 +738,10 @@
 				if (control.isEmpty()) {
 					// no-one granted, no-one waiting, remove lock control
 					locks.remove(control.getLockable());
+                    entry.control = null;
 				}
 				return;
 			}
-		} // synchronized (this)
 
 		if (tryGrant && (nextGrant != null)) {
 			nextGrant.wakeUp(Constants.WAITING_LOCK_GRANT);
@@ -556,26 +759,36 @@
      * @return the corresponding lock in the group map, or <code>null</code>
if
      * the object was not unlocked
      */
-    public synchronized Lock unlockReference(CompatibilitySpace space,
-                                             Lockable ref, Object qualifier,
-                                             Map group) {
+    public Lock unlockReference(CompatibilitySpace space, Lockable ref,
+                                Object qualifier, Map group) {
 
-        Control control = getControl(ref);
-        if (control == null) {
+        Entry entry = locks.get(ref);
+        if (entry == null) {
             return null;
         }
 
-        Lock setLock = control.getLock(space, qualifier);
-        if (setLock == null) {
-            return null;
-        }
+        entry.lock();
+        try {
+            Control control = entry.control;
+            if (control == null) {
+                return null;
+            }
 
-        Lock lockInGroup = (Lock) group.remove(setLock);
-        if (lockInGroup != null) {
-            unlock(lockInGroup, 1);
-        }
+            Lock setLock = control.getLock(space, qualifier);
+            if (setLock == null) {
+                return null;
+            }
 
-        return lockInGroup;
+            Lock lockInGroup = (Lock) group.remove(setLock);
+            if (lockInGroup != null) {
+                unlock(entry, lockInGroup, 1);
+            }
+
+            return lockInGroup;
+
+        } finally {
+            entry.unlock();
+        }
     }
 
     /**
@@ -626,8 +839,14 @@
         // be granted then we do the slow join the queue and
         // release the lock method.
 
-        synchronized (this) {
-            Control control = getControl(ref);
+        Entry entry = locks.get(ref);
+        if (entry == null) {
+            return true;
+        }
+
+        entry.lock();
+        try {
+            Control control = entry.control;
             if (control == null) {
                 return true;
             }
@@ -644,6 +863,8 @@
             if (timeout == C_LockFactory.NO_WAIT) {
                 return false;
             }
+        } finally {
+            entry.unlock();
         }
 
         Lock lock = lockObject(space, ref, qualifier, timeout);
@@ -692,17 +913,22 @@
 	}			
 //EXCLUDE-END-lockdiag- 
 
-    public String toDebugString()
+    private String toDebugString()
     {
         if (SanityManager.DEBUG)
         {
             String str = new String();
 
             int i = 0;
-            for (Iterator it = locks.values().iterator(); it.hasNext(); )
+            for (Entry entry : locks.values())
             {
-                str += "\n  lock[" + i + "]: " + 
-                    DiagnosticUtil.toDiagString(it.next());
+                entry.lock();
+                try {
+                    str += "\n  lock[" + i + "]: " +
+                        DiagnosticUtil.toDiagString(entry.control);
+                } finally {
+                    entry.unlock();
+                }
             }
 
             return(str);
@@ -715,13 +941,20 @@
 
     /**
      * Add all waiters in this lock table to a <code>Map</code> object.
-     * <br>
-     * MT - must be synchronized on this <code>LockSet</code> object.
+     * This method can only be called by the thread that is currently
+     * performing deadlock detection. All entries that are visited in the lock
+     * table will be locked when this method returns. The entries that have
+     * been seen and locked will be unlocked after the deadlock detection has
+     * finished.
      */
     public void addWaiters(Map waiters) {
-        for (Iterator it = locks.values().iterator(); it.hasNext(); ) {
-            Control control = (Control) it.next();
-            control.addWaiters(waiters);
+        seenByDeadlockDetection = new ArrayList<Entry>(locks.size());
+        for (Entry entry : locks.values()) {
+            seenByDeadlockDetection.add(entry);
+            entry.lockForDeadlockDetection();
+            if (entry.control != null) {
+                entry.control.addWaiters(waiters);
+            }
         }
     }
 
@@ -729,60 +962,50 @@
 	/**
 	 * make a shallow clone of myself and my lock controls
 	 */
-	public synchronized Map shallowClone()
-	{
-		HashMap clone = new HashMap();
-
-		for (Iterator it = locks.keySet().iterator(); it.hasNext(); )
-		{
-			Lockable lockable = (Lockable) it.next();
-			Control control = getControl(lockable);
+    public Map<Lockable, Control> shallowClone() {
+        HashMap<Lockable, Control> clone = new HashMap<Lockable, Control>();
 
-			clone.put(lockable, control.shallowClone());
+        for (Entry entry : locks.values()) {
+            entry.lock();
+            try {
+                Control control = entry.control;
+                if (control != null) {
+                    clone.put(control.getLockable(), control.shallowClone());
+                }
+            } finally {
+                entry.unlock();
+            }
 		}
 
 		return clone;
 	}
 //EXCLUDE-END-lockdiag- 
 
-	/*
-	** Support for anyoneBlocked(). These methods assume that caller
-	** is synchronized on this LockSet object.
-	*/
-
 	/**
 	 * Increase blockCount by one.
-	 * <BR>
-	 * MT - must be synchronized on this <code>LockSet</code> object.
 	 */
 	public void oneMoreWaiter() {
-		blockCount++;
+        blockCount.incrementAndGet();
 	}
 
 	/**
 	 * Decrease blockCount by one.
-	 * <BR>
-	 * MT - must be synchronized on this <code>LockSet</code> object.
 	 */
 	public void oneLessWaiter() {
-		blockCount--;
+		blockCount.decrementAndGet();
 	}
 
-	public synchronized boolean anyoneBlocked() {
+    /**
+     * Check whether anyone is blocked.
+     * @return <code>true</code> if someone is blocked, <code>false</code>
+     * otherwise
+     */
+	public boolean anyoneBlocked() {
+        int blocked = blockCount.get();
 		if (SanityManager.DEBUG) {
 			SanityManager.ASSERT(
-				blockCount >= 0, "blockCount should not be negative");
+				blocked >= 0, "blockCount should not be negative");
 		}
-
-		return blockCount != 0;
-	}
-
-	/**
-	 * Get the <code>Control</code> for an object in the lock table.
-	 * <br>
-	 * MT - must be synchronized on this <code>LockSet</code> object.
-	 */
-	private final Control getControl(Lockable ref) {
-		return (Control) locks.get(ref);
+		return blocked != 0;
 	}
 }

Added: db/derby/code/trunk/java/engine/org/apache/derby/impl/services/locks/ConcurrentPool.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/services/locks/ConcurrentPool.java?view=auto&rev=535851
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/services/locks/ConcurrentPool.java
(added)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/services/locks/ConcurrentPool.java
Mon May  7 04:28:40 2007
@@ -0,0 +1,37 @@
+/*
+
+   Derby - Class org.apache.derby.impl.services.locks.ConcurrentPool
+
+   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.derby.impl.services.locks;
+
+/**
+ * A <code>LockFactory</code> which allows multiple threads to enter without
+ * blocking each other out.
+ */
+public final class ConcurrentPool extends AbstractPool {
+    /**
+     * Create the <code>ConcurrentLockSet</code> object that keeps the locks.
+     *
+     * @return a <code>ConcurrentLockSet</code>
+     */
+    protected LockTable createLockTable() {
+        return new ConcurrentLockSet(this);
+    }
+}

Propchange: db/derby/code/trunk/java/engine/org/apache/derby/impl/services/locks/ConcurrentPool.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/services/locks/Deadlock.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/services/locks/Deadlock.java?view=diff&rev=535851&r1=535850&r2=535851
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/services/locks/Deadlock.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/services/locks/Deadlock.java Mon
May  7 04:28:40 2007
@@ -48,7 +48,14 @@
 	/**
 	 * Look for a deadlock.
 	 * <BR>
-	 * MT - must be synchronized on the <code>LockSet</code> object.
+	 * MT - if the <code>LockTable</code> is a <code>LockSet</code>
object, the
+	 * callers must be synchronized on the <code>LockSet</code> object in order
+	 * to satisfy the syncronization requirements of
+	 * <code>LockSet.addWaiters()</code>. If it is a
+	 * <code>ConcurrentLockSet</code> object, the callers must not hold any of
+	 * the <code>ReentrantLock</code>s guarding the entries in the lock table,
+	 * and the callers must make sure that only a single thread calls
+	 * <code>look()</code> at a time.
 	 */
 	static Object[] look(AbstractPool factory, LockTable set,
 						 LockControl control, ActiveLock startingLock,

Modified: db/derby/code/trunk/java/engine/org/apache/derby/modules.properties
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/modules.properties?view=diff&rev=535851&r1=535850&r2=535851
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/modules.properties (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/modules.properties Mon May  7 04:28:40
2007
@@ -138,8 +138,14 @@
 derby.module.javaCompiler=org.apache.derby.impl.services.bytecode.BCJava
 cloudscape.config.javaCompiler=all
 
-derby.module.lockManager=org.apache.derby.impl.services.locks.SinglePool
-cloudscape.config.lockManager=all
+derby.module.lockManagerJ1=org.apache.derby.impl.services.locks.SinglePool
+derby.env.jdk.lockManagerJ1=1
+cloudscape.config.lockManagerJ1=all
+
+# ConcurrentPool requires JDK 1.5 (constant 6)
+#jdk15_optional_derby.module.lockManagerJ6=org.apache.derby.impl.services.locks.ConcurrentPool
+#jdk15_optional_derby.env.jdk.lockManagerJ6=6
+#jdk15_optional_cloudscape.config.lockManagerJ6=all
 
 derby.module.classManagerJ2=org.apache.derby.impl.services.reflect.ReflectClassesJava2
 cloudscape.config.classManagerJ2=derby



Mime
View raw message