ozeigermann    2004/10/31 02:11:56

  Modified:    transaction/src/java/org/apache/commons/transaction/locking
                        GenericLock.java
  Added:       transaction/src/test/org/apache/commons/transaction/locking
                        GenericLockTest.java
  Log:
  Added new compatibility mode for generic and basic test for locking (finally...)
  
  Revision  Changes    Path
  1.1                  
jakarta-commons-sandbox/transaction/src/test/org/apache/commons/transaction/locking/GenericLockTest.java
  
  Index: GenericLockTest.java
  ===================================================================
  /*
   * $Header: 
/home/cvs/jakarta-commons-sandbox/transaction/src/test/org/apache/commons/transaction/locking/GenericLockTest.java,v
 1.1 2004/10/31 10:11:56 ozeigermann Exp $
   * $Revision: 1.1 $
   * $Date: 2004/10/31 10:11:56 $
   *
   * ====================================================================
   *
   * Copyright 2004 The Apache Software Foundation 
   *
   * Licensed 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.commons.transaction.locking;
  
  import java.io.PrintWriter;
  
  import junit.framework.Test;
  import junit.framework.TestCase;
  import junit.framework.TestSuite;
  
  import org.apache.commons.transaction.util.LoggerFacade;
  import org.apache.commons.transaction.util.PrintWriterLogger;
  
  /**
   * Tests for generic locks. 
   *
   * @version $Revision: 1.1 $
   */
  public class GenericLockTest extends TestCase {
  
      private static final LoggerFacade sLogger = new PrintWriterLogger(new 
PrintWriter(System.out),
              GenericLockTest.class.getName(), false);
  
      protected static final int READ_LOCK = 1;
      protected static final int WRITE_LOCK = 2;
      
      public static Test suite() {
          TestSuite suite = new TestSuite(GenericLockTest.class);
          return suite;
      }
  
      public static void main(java.lang.String[] args) {
          junit.textui.TestRunner.run(suite());
      }
  
      public GenericLockTest(String testName) {
          super(testName);
      }
  
      // we do not wait, as we only want the check the results and do not want real 
locking
      protected boolean acquireNoWait(GenericLock lock, String owner, int 
targetLockLevel)  {
          try {
              return lock.acquire(owner, targetLockLevel, false, true, -1);
          } catch (InterruptedException e) {
              return false;
          }
      }
      
      public void testBasic() throws Throwable {
          String owner1 = "owner1";
          String owner2 = "owner2";
          String owner3 = "owner3";
          
          // a read / write lock
          GenericLock lock = new GenericLock("Test read write lock", WRITE_LOCK, 
sLogger);
          
          // of course more than one can read
          boolean canRead1 = acquireNoWait(lock, owner1, READ_LOCK);
          assertTrue(canRead1);
          boolean canRead2 = acquireNoWait(lock, owner2, READ_LOCK);
          assertTrue(canRead2);
          
          // as there already are read locks, this write should not be possible
          boolean canWrite3 = acquireNoWait(lock, owner3, WRITE_LOCK);
          assertFalse(canWrite3);
          
          // release one read lock
          lock.release(owner2);
          // this should not change anything with the write as there is still one read 
lock left
          canWrite3 = acquireNoWait(lock, owner3, WRITE_LOCK);
          assertFalse(canWrite3);
  
          // release the other and final read lock as well
          lock.release(owner1);
          // no we should be able to get write access
          canWrite3 = acquireNoWait(lock, owner3, WRITE_LOCK);
          assertTrue(canWrite3);
          // but of course no more read access
          canRead2 = acquireNoWait(lock, owner2, READ_LOCK);
          assertFalse(canRead2);
          
          // relase the write lock and make sure we can read again
          lock.release(owner3);
          canRead2 = acquireNoWait(lock, owner2, READ_LOCK);
          assertTrue(canRead2);
          
          // now we do something weired, we try to block all locks lower than write...
          boolean canBlock3 = lock.acquire(owner3, WRITE_LOCK, false, 
GenericLock.COMPATIBILITY_SUPPORT, -1);
          // which of course does not work, as there already is an incompatible read 
lock
          assertFalse(canBlock3);
          
          // ok, release read lock (no we have no more locks) and try again
          lock.release(owner2);
          canBlock3 = lock.acquire(owner3, WRITE_LOCK, false, 
GenericLock.COMPATIBILITY_SUPPORT, -1);
          // which now should work creating an ordinary lock
          assertTrue(canBlock3);
          
          // as this just an ordinary lock, we should not get a read lock:
          canRead1 = acquireNoWait(lock, owner1, READ_LOCK);
          assertFalse(canRead1);
          
          // this is the trick now, we *can* get an addtional write lock with this 
request as it has
          // the same level as the write lock already set. This works, as we do not 
care for the
          // write lock level, but only want to inhibit the read lock:
          boolean canBlock2 = lock.acquire(owner2, WRITE_LOCK, false, 
GenericLock.COMPATIBILITY_SUPPORT, -1);
          assertTrue(canBlock2);
          
          // now if we release one of the blocks supporting each other we still should 
not get a
          // read lock
          lock.release(owner3);
          canRead1 = acquireNoWait(lock, owner1, READ_LOCK);
          assertFalse(canRead1);
  
          // but of course after we release the second as well
          lock.release(owner2);
          canRead1 = acquireNoWait(lock, owner1, READ_LOCK);
          assertTrue(canRead1);
      }
  }
  
  
  
  1.6       +89 -15    
jakarta-commons-sandbox/transaction/src/java/org/apache/commons/transaction/locking/GenericLock.java
  
  Index: GenericLock.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-commons-sandbox/transaction/src/java/org/apache/commons/transaction/locking/GenericLock.java,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- GenericLock.java  31 Oct 2004 07:31:42 -0000      1.5
  +++ GenericLock.java  31 Oct 2004 10:11:56 -0000      1.6
  @@ -104,6 +104,11 @@
    */
   public class GenericLock implements MultiLevelLock {
   
  +    public static final int COMPATIBILITY_NONE = 0;
  +    public static final int COMPATIBILITY_REENTRANT = 1;
  +    public static final int COMPATIBILITY_SUPPORT = 2;
  +    public static final int COMPATIBILITY_REENTRANT_AND_SUPPORT = 3;
  +    
       private Object resourceId;
       private Map owners = new HashMap();
       private int maxLockLevel;
  @@ -125,11 +130,61 @@
           this.logger = logger;
       }
   
  +    /**
  +     * Tests if a certain lock level could be acquired.
  +     * 
  +     * @param ownerId a unique id identifying the entity that wants to acquire a 
certain lock level on this lock
  +     * @param targetLockLevel the lock level to acquire
  +     * @param reentrant <code>true</code> if lock levels of the same entity 
acquired earlier 
  +     * @return <code>true</code> if the lock could be acquired acquired at the time 
this method
  +     * was called
  +     */
  +    public boolean test(Object ownerId, int targetLockLevel, int compatibility) {
  +        boolean success = false;
  +        try {
  +            success = tryLock(ownerId, targetLockLevel, compatibility);
  +        } finally {
  +            release(ownerId);
  +        }
  +        return success;
  +    }
  +    
  +    /**
  +     * @see 
org.apache.commons.transaction.locking.MultiLevelLock#acquire(java.lang.Object,
  +     *      int, boolean, boolean, long)
  +     */
  +    public synchronized boolean acquire(Object ownerId, int targetLockLevel, 
boolean wait,
  +            boolean reentrant, long timeoutMSecs) throws InterruptedException {
  +        return acquire(ownerId, targetLockLevel, wait, reentrant ? 
COMPATIBILITY_REENTRANT
  +                : COMPATIBILITY_NONE, timeoutMSecs);
  +    }
  +        
  +    /**
  +     * Tries to acquire a certain lock level on this lock. Does the same as
  +     * [EMAIL PROTECTED] 
org.apache.commons.transaction.locking.MultiLevelLock#acquire(java.lang.Object, int, 
boolean, boolean, long)}
  +     * except that it allows for different compatibility settings. There is an
  +     * additional compatibility mode [EMAIL PROTECTED] #COMPATIBILITY_SUPPORT} that 
allows
  +     * equal lock levels not to interfere with each other. This is like an
  +     * additional shared compatibility and useful when you only want to make sure 
not to interfer
  +     * with lowe levels, but are fine with the same.
  +     * 
  +     * @param compatibility 
  +     *            [EMAIL PROTECTED] #COMPATIBILITY_NONE} if no additional 
compatibility is
  +     *            desired (same as reentrant set to false) ,
  +     *            [EMAIL PROTECTED] #COMPATIBILITY_REENTRANT} if lock level by the 
same
  +     *            owner shall not affect compatibility (same as reentrant set to
  +     *            true), or [EMAIL PROTECTED] #COMPATIBILITY_SUPPORT} if lock 
levels that
  +     *            are the same as the desired shall not affect compatibility, or 
finally
  +     * [EMAIL PROTECTED] #COMPATIBILITY_REENTRANT_AND_SUPPORT} which is a 
combination of reentrant and support
  +     * 
  +     * @see 
org.apache.commons.transaction.locking.MultiLevelLock#acquire(java.lang.Object,
  +     *      int, boolean, boolean, long)
  +     */
       public synchronized boolean acquire(
           Object ownerId,
           int targetLockLevel,
           boolean wait,
  -        boolean reentrant,
  +        int compatibility,
           long timeoutMSecs)
           throws InterruptedException {
   
  @@ -142,7 +197,7 @@
                   + " at "
                   + System.currentTimeMillis());
   
  -        if (tryLock(ownerId, targetLockLevel, reentrant)) {
  +        if (tryLock(ownerId, targetLockLevel, compatibility)) {
   
               logger.logFiner(
                   ownerId.toString()
  @@ -171,7 +226,7 @@
                               + System.currentTimeMillis());
   
                       wait(remaining);
  -                    if (tryLock(ownerId, targetLockLevel, reentrant)) {
  +                    if (tryLock(ownerId, targetLockLevel, compatibility)) {
   
                           logger.logFiner(
                               ownerId.toString()
  @@ -188,6 +243,9 @@
           }
       }
   
  +    /**
  +     * @see org.apache.commons.transaction.locking.MultiLevelLock#release(Object)
  +     */
       public synchronized void release(Object ownerId) {
           if (owners.remove(ownerId) != null) {
               logger.logFiner(
  @@ -200,6 +258,9 @@
           }
       }
   
  +    /**
  +     * @see 
org.apache.commons.transaction.locking.MultiLevelLock#getLockLevel(Object)
  +     */
       public synchronized int getLockLevel(Object ownerId) {
           LockOwner owner = (LockOwner) owners.get(ownerId);
           if (owner == null) {
  @@ -256,20 +317,29 @@
       }
   
       protected synchronized LockOwner getMaxLevelOwner() {
  -        return getMaxLevelOwnerNotMe(null);
  +        return getMaxLevelOwner(null, -1);
       }
   
  -    protected synchronized LockOwner getMaxLevelOwnerNotMe(LockOwner me) {
  +    protected synchronized LockOwner getMaxLevelOwner(LockOwner reentrantOwner) {
  +        return getMaxLevelOwner(reentrantOwner, -1);
  +    }
  +
  +    protected synchronized LockOwner getMaxLevelOwner(int supportLockLevel) {
  +        return getMaxLevelOwner(null, supportLockLevel);
  +    }
  +
  +    protected synchronized LockOwner getMaxLevelOwner(LockOwner reentrantOwner, int 
supportLockLevel) {
           LockOwner maxOwner = null;
           for (Iterator it = owners.values().iterator(); it.hasNext();) {
               LockOwner owner = (LockOwner) it.next();
  -            if (!owner.equals(me) && (maxOwner == null || maxOwner.lockLevel < 
owner.lockLevel)) {
  +            if (owner.lockLevel != supportLockLevel && !owner.equals(reentrantOwner)
  +                    && (maxOwner == null || maxOwner.lockLevel < owner.lockLevel)) {
                   maxOwner = owner;
               }
           }
           return maxOwner;
       }
  -
  +    
       protected synchronized void setLockLevel(Object ownerId, LockOwner lock, int 
targetLockLevel) {
           // be sure there exists at most one lock per owner
           if (lock != null) {
  @@ -299,20 +369,24 @@
           }
       }
   
  -    protected synchronized boolean tryLock(Object ownerId, int targetLockLevel, 
boolean reentrant) {
  +    protected synchronized boolean tryLock(Object ownerId, int targetLockLevel, int 
compatibility) {
   
           LockOwner myLock = (LockOwner) owners.get(ownerId);
   
           // determine highest owner        
           LockOwner highestOwner;
  -        if (myLock != null && reentrant) {
  -            if (targetLockLevel <= myLock.lockLevel) {
  +        if (compatibility == COMPATIBILITY_REENTRANT) {
  +            if (myLock != null && targetLockLevel <= myLock.lockLevel) {
                   // we already have it
                   return true;
               } else {
                   // our own lock will not be compromised by ourself's
  -                highestOwner = getMaxLevelOwnerNotMe(myLock);
  +                highestOwner = getMaxLevelOwner(myLock);
               }
  +        } else if (compatibility == COMPATIBILITY_SUPPORT) {
  +            // we are compatible with any other lock owner holding
  +            // the same lock level
  +            highestOwner = getMaxLevelOwner(targetLockLevel);
           } else {
               highestOwner = getMaxLevelOwner();
           }
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to