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]