[jboss-cvs] JBossCache/src/org/jboss/cache/interceptors ...
Brian Stansberry
brian.stansberry at jboss.com
Wed Nov 22 16:37:37 EST 2006
User: bstansberry
Date: 06/11/22 16:37:37
Modified: src/org/jboss/cache/interceptors Tag:
JBossCache_1_3_0_SP3_JBCACHE-873
PessimisticLockInterceptor.java
Log:
[JBCACHE-873] Fix JBCACHE-871 for Siemens
Revision Changes Path
No revision
No revision
1.14.6.1 +88 -31 JBossCache/src/org/jboss/cache/interceptors/PessimisticLockInterceptor.java
(In the diff below, changes in quantity of whitespace are not shown.)
Index: PessimisticLockInterceptor.java
===================================================================
RCS file: /cvsroot/jboss/JBossCache/src/org/jboss/cache/interceptors/PessimisticLockInterceptor.java,v
retrieving revision 1.14
retrieving revision 1.14.6.1
diff -u -b -r1.14 -r1.14.6.1
--- PessimisticLockInterceptor.java 14 Feb 2006 08:10:51 -0000 1.14
+++ PessimisticLockInterceptor.java 22 Nov 2006 21:37:37 -0000 1.14.6.1
@@ -13,6 +13,8 @@
import org.jboss.cache.lock.TimeoutException;
import org.jgroups.blocks.MethodCall;
+import EDU.oswego.cs.dl.util.concurrent.ConcurrentHashMap;
+
import javax.transaction.Transaction;
import java.lang.reflect.Method;
import java.util.*;
@@ -22,7 +24,7 @@
* scope of the TX. When no TX is present, we keep track of the locks acquired during the current method and unlock
* when the method returns
* @author Bela Ban
- * @version $Id: PessimisticLockInterceptor.java,v 1.14 2006/02/14 08:10:51 bela Exp $
+ * @version $Id: PessimisticLockInterceptor.java,v 1.14.6.1 2006/11/22 21:37:37 bstansberry Exp $
*/
public class PessimisticLockInterceptor extends Interceptor {
TransactionTable tx_table=null;
@@ -30,6 +32,7 @@
/** Map<Object, java.util.List>. Keys = threads, values = lists of locks held by that thread */
Map lock_table;
private long lock_acquisition_timeout;
+ Map removedNodes = new ConcurrentHashMap();
public void setCache(TreeCache cache) {
@@ -60,6 +63,7 @@
boolean recursive=false;
boolean createIfNotExists=false;
+ boolean storeLockedNode = false;
// 1. Determine the type of lock (read, write, or none) depending on the method. If no lock is required, invoke
// the method, then return immediately
@@ -76,6 +80,8 @@
fqn=(Fqn)args[1];
lock_type=DataNode.LOCK_TYPE_WRITE;
recursive=true; // remove node and *all* child nodes
+ // JBCACHE-871 We need to store the node
+ storeLockedNode = true;
}
else if(meth.equals(TreeCache.removeKeyMethodLocal) || meth.equals(TreeCache.removeDataMethodLocal)) {
fqn=(Fqn)args[1];
@@ -129,13 +135,13 @@
if(fqn != null) {
if(createIfNotExists) {
do {
- lock(fqn, ctx.getGlobalTransaction(), lock_type, recursive, lock_timeout, createIfNotExists);
+ lock(fqn, ctx.getGlobalTransaction(), lock_type, recursive, lock_timeout, createIfNotExists, storeLockedNode);
}
while(!cache.exists(fqn)); // keep trying until we have the lock (fixes concurrent remove())
// terminates successfully, or with (Timeout)Exception
}
else
- lock(fqn, ctx.getGlobalTransaction(), lock_type, recursive, lock_timeout, createIfNotExists);
+ lock(fqn, ctx.getGlobalTransaction(), lock_type, recursive, lock_timeout, createIfNotExists, storeLockedNode);
}
else {
if(log.isTraceEnabled())
@@ -144,6 +150,14 @@
if(meth.equals(TreeCache.lockMethodLocal))
return null;
retval=super.invoke(m);
+
+ // FIXME this should be done in UnlockInterceptor, but I didn't want
+ // to add the removedNodes map to TreeCache
+ if (storeLockedNode && ctx.getGlobalTransaction() == null)
+ {
+ removedNodes.remove(fqn);
+ }
+
return retval;
}
@@ -157,13 +171,14 @@
* @param recursive Lock children recursively
*/
private void lock(Fqn fqn, GlobalTransaction gtx, int lock_type, boolean recursive,
- long lock_timeout, boolean createIfNotExists)
+ long lock_timeout, boolean createIfNotExists, boolean storeRemovalNode)
throws TimeoutException, LockingException, InterruptedException {
DataNode n, child_node=null;
Object child_name, owner=gtx != null? gtx : (Object)Thread.currentThread();
int treeNodeSize;
boolean acquired=false;
IsolationLevel isolation_level;
+ int currentLockType;
if (log.isTraceEnabled()) log.trace("Attempting to lock node " + fqn + " for owner " + owner);
@@ -182,7 +197,20 @@
n=cache.getRoot();
for(int i=0; i < treeNodeSize; i++) {
child_name=fqn.get(i);
- child_node=(DataNode) n.getOrCreateChild(child_name, gtx, createIfNotExists);
+ child_node = (DataNode) n.getChild(child_name);
+ if (child_node == null)
+ {
+ // JBCACHE-871 -- If this is a node removed by another incomplete
+ // invocation or an uncommitted transaction, we won't be able to find
+ // the node in the cache; so first check for it in the removedNodes map
+ DataNode removed = (DataNode) removedNodes.get(fqn.getFqnChild(i +1));
+ if (removed != null && lock_type != DataNode.LOCK_TYPE_NONE)
+ {
+ acquireNodeLock(removed, owner, gtx, lock_type, lock_timeout);
+ }
+ child_node = (DataNode) n.getOrCreateChild(child_name, gtx, createIfNotExists);
+ }
+
if(child_node == null) {
if(log.isTraceEnabled())
log.trace("failed to find or create child " + child_name + " of node " + n.getFqn());
@@ -194,34 +222,19 @@
n=child_node;
continue;
}
- else {
- if(lock_type == DataNode.LOCK_TYPE_WRITE && i == (treeNodeSize - 1)) {
- acquired=child_node.acquire(owner, lock_timeout, DataNode.LOCK_TYPE_WRITE);
- }
- else {
- acquired=child_node.acquire(owner, lock_timeout, DataNode.LOCK_TYPE_READ);
- }
- }
-
- if(acquired) {
- if(gtx != null) {
- // add the lock to the list of locks maintained for this transaction
- // (needed for release of locks on commit or rollback)
- cache.getTransactionTable().addLock(gtx, child_node.getLock());
- }
- else {
- IdentityLock l=child_node.getLock();
- List locks=(List)lock_table.get(Thread.currentThread());
- if(locks == null) {
- locks=Collections.synchronizedList(new LinkedList());
- lock_table.put(Thread.currentThread(), locks);
- }
- if(!locks.contains(l))
- locks.add(l);
+ if (lock_type == DataNode.LOCK_TYPE_WRITE && i == (treeNodeSize - 1))
+ {
+ currentLockType = DataNode.LOCK_TYPE_WRITE;
}
+ else
+ {
+ currentLockType = DataNode.LOCK_TYPE_READ;
}
+ // Try to acquire the lock; recording that we did if successful
+ acquireNodeLock(child_node, owner, gtx, currentLockType, lock_timeout);
+
if(recursive && i == (treeNodeSize - 1)) {
Set acquired_locks=child_node.acquireAll(owner, lock_timeout, lock_type);
if(acquired_locks.size() > 0) {
@@ -240,6 +253,50 @@
}
n=child_node;
}
+
+ if (storeRemovalNode && n != null)
+ {
+ removedNodes.put(fqn, n);
+ }
+ }
+
+ private void acquireNodeLock(DataNode node, Object owner, GlobalTransaction gtx, int lock_type, long lock_timeout) throws LockingException, TimeoutException, InterruptedException
+ {
+ boolean acquired = node.acquire(owner, lock_timeout, lock_type);
+ if (acquired)
+ {
+ // Record the lock for release on method return or tx commit/rollback
+ recordNodeLock(gtx, node.getLock());
+ }
+ }
+
+ private void recordNodeLock(GlobalTransaction gtx, IdentityLock lock)
+ {
+ if (gtx != null)
+ {
+ // add the lock to the list of locks maintained for this transaction
+ // (needed for release of locks on commit or rollback)
+ cache.getTransactionTable().addLock(gtx, lock);
+ }
+ else
+ {
+ List locks = getLocks(Thread.currentThread());
+ if (!locks.contains(lock))
+ locks.add(lock);
+ }
+ }
+
+ private List getLocks(Thread currentThread)
+ {
+ // This sort of looks like a get/put race condition, but
+ // since we key off the Thread, it's not
+ List locks = (List) lock_table.get(currentThread);
+ if (locks == null)
+ {
+ locks = Collections.synchronizedList(new LinkedList());
+ lock_table.put(currentThread, locks);
+ }
+ return locks;
}
More information about the jboss-cvs-commits
mailing list