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]