[jboss-cvs] JBossCache/src/org/jboss/cache/interceptors ...
Manik Surtani
msurtani at jboss.com
Thu Dec 14 12:18:48 EST 2006
User: msurtani
Date: 06/12/14 12:18:48
Modified: src/org/jboss/cache/interceptors
PessimisticLockInterceptor.java
OptimisticNodeInterceptor.java
Log:
The beginnings of porting JBCACHE-871 and JBCACHE-875 as well as rearranging the Node/Cache object model to something sensible
Revision Changes Path
1.37 +150 -47 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.36
retrieving revision 1.37
diff -u -b -r1.36 -r1.37
--- PessimisticLockInterceptor.java 12 Dec 2006 14:51:44 -0000 1.36
+++ PessimisticLockInterceptor.java 14 Dec 2006 17:18:48 -0000 1.37
@@ -7,12 +7,14 @@
package org.jboss.cache.interceptors;
import org.jboss.cache.CacheSPI;
+import org.jboss.cache.DataNode;
import org.jboss.cache.Fqn;
import org.jboss.cache.GlobalTransaction;
import org.jboss.cache.InvocationContext;
import org.jboss.cache.Node;
import org.jboss.cache.TransactionEntry;
import org.jboss.cache.TransactionTable;
+import org.jboss.cache.TreeCacheProxyImpl;
import org.jboss.cache.lock.IsolationLevel;
import org.jboss.cache.lock.LockingException;
import org.jboss.cache.lock.NodeLock;
@@ -22,6 +24,7 @@
import javax.transaction.Transaction;
import java.util.Collections;
+import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -34,7 +37,7 @@
* current method and unlock when the method returns.
*
* @author Bela Ban
- * @version $Id: PessimisticLockInterceptor.java,v 1.36 2006/12/12 14:51:44 msurtani Exp $
+ * @version $Id: PessimisticLockInterceptor.java,v 1.37 2006/12/14 17:18:48 msurtani Exp $
*/
public class PessimisticLockInterceptor extends Interceptor
{
@@ -89,7 +92,8 @@
boolean recursive = false;
boolean createIfNotExists = false;
- boolean zeroLockTimeout = false; // only used if the call is an evict() call. See JBCACHE-794
+ boolean zeroLockTimeout = false;// only used if the call is an evict() call. See JBCACHE-794
+ boolean isDeleteOperation = false;// needed for JBCACHE-871
// 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
@@ -108,9 +112,10 @@
lock_type = NodeLock.LockType.WRITE;
break;
case MethodDeclarations.removeNodeMethodLocal_id:
+ isDeleteOperation = true;
fqn = (Fqn) args[1];
lock_type = NodeLock.LockType.WRITE;
- recursive = true; // remove node and *all* child nodes
+ recursive = true;// remove node and *all* child nodes
break;
case MethodDeclarations.removeKeyMethodLocal_id:
case MethodDeclarations.removeDataMethodLocal_id:
@@ -162,9 +167,9 @@
{
do
{
- lock(fqn, ctx.getGlobalTransaction(), lock_type, recursive, createIfNotExists, zeroLockTimeout ? 0 : lock_acquisition_timeout);
+ lock(fqn, ctx.getGlobalTransaction(), lock_type, recursive, createIfNotExists, zeroLockTimeout ? 0 : lock_acquisition_timeout, isDeleteOperation);
}
- while (createIfNotExists && !cache.hasChild(fqn)); // keep trying until we have the lock (fixes concurrent remove())
+ while (createIfNotExists && !cache.hasChild(fqn));// keep trying until we have the lock (fixes concurrent remove())
}
else if (!lockNecessary)
{
@@ -177,7 +182,18 @@
{
return null;
}
- return super.invoke(m);
+ Object o = super.invoke(m);
+ // FIXME this should be done in UnlockInterceptor, but I didn't want
+ // to add the removedNodes map to TreeCache
+ if (isDeleteOperation && ctx.getGlobalTransaction() == null)
+ {
+ //cache.getRemovedNodesMap().remove(fqn);
+ //cache.peek(fqn);
+ // do a REAL remove here.
+ cache.peek(fqn).getNodeSPI().getLock().releaseAll(Thread.currentThread());
+ ((TreeCacheProxyImpl) cache).realRemove(fqn, true);
+ }
+ return o;
}
private void obtainLocksForMove(Fqn parent, Fqn node) throws InterruptedException
@@ -187,11 +203,11 @@
// this call will ensure the node gets a WL and it's current parent gets RL.
if (log.isTraceEnabled()) log.trace("Attempting to get WL on node to be moved [" + node + "]");
- lock(node, cache.getInvocationContext().getGlobalTransaction(), NodeLock.LockType.WRITE, true, false, lock_acquisition_timeout);
+ lock(node, cache.getInvocationContext().getGlobalTransaction(), NodeLock.LockType.WRITE, true, false, lock_acquisition_timeout, false);
//now for an RL for the new parent.
if (log.isTraceEnabled()) log.trace("Attempting to get RL on new parent [" + parent + "]");
- lock(parent, cache.getInvocationContext().getGlobalTransaction(), NodeLock.LockType.READ, true, false, lock_acquisition_timeout);
+ lock(parent, cache.getInvocationContext().getGlobalTransaction(), NodeLock.LockType.READ, true, false, lock_acquisition_timeout, false);
}
@@ -203,7 +219,7 @@
* @param lock_type DataNode.LOCK_TYPE_READ, DataNode.LOCK_TYPE_WRITE or DataNode.LOCK_TYPE_NONE
* @param recursive Lock children recursively
*/
- private void lock(Fqn fqn, GlobalTransaction gtx, NodeLock.LockType lock_type, boolean recursive, boolean createIfNotExists, long timeout)
+ private void lock(Fqn fqn, GlobalTransaction gtx, NodeLock.LockType lock_type, boolean recursive, boolean createIfNotExists, long timeout, boolean isDeleteOperation)
throws TimeoutException, LockingException, InterruptedException
{
Node n;
@@ -212,8 +228,6 @@
Thread currentThread = Thread.currentThread();
Object owner = (gtx != null) ? gtx : currentThread;
int treeNodeSize;
- boolean acquired;
-
if (log.isTraceEnabled()) log.trace("Attempting to lock node " + fqn + " for owner " + owner);
@@ -235,11 +249,19 @@
n = cache;
treeNodeSize = fqn.size();
- for (int i = 0; i < treeNodeSize; i++)
+ for (int i = -1; i < treeNodeSize; i++)
+ {
+ if (i == -1)
+ {
+ // this is the root node
+ child_name = Fqn.ROOT.getName();
+ child_node = cache.getRoot();
+ }
+ else
{
child_name = fqn.get(i);
-
child_node = n.getNodeSPI().getChildrenMap().get(child_name);
+ }
/*
cache.getInvocationContext().getOptionOverrides().setBypassInterceptorChain(true);
Fqn childFqn = new Fqn(child_name);
@@ -260,6 +282,7 @@
return;
}
+ NodeLock.LockType lockTypeRequired;
if (lock_type == NodeLock.LockType.NONE)
{
// acquired=false;
@@ -268,56 +291,110 @@
}
else
{
- if (lock_type == NodeLock.LockType.WRITE && i == (treeNodeSize - 1))
+ if (writeLockNeeded(lock_type, i, treeNodeSize, isDeleteOperation, createIfNotExists, fqn, child_node.getFqn()))
{
//acquired=child_node.acquire(owner, lock_timeout, DataNode.LOCK_TYPE_WRITE);
- acquired = lockManager.acquire(child_node, owner, NodeLock.LockType.WRITE, timeout);
+ //acquired = lockManager.acquire(child_node, owner, NodeLock.LockType.WRITE, timeout);
+ lockTypeRequired = NodeLock.LockType.WRITE;
+
}
else
{
//acquired=child_node.acquire(owner, lock_timeout, DataNode.LOCK_TYPE_READ);
- acquired = lockManager.acquire(child_node, owner, NodeLock.LockType.READ, timeout);
+ //acquired = lockManager.acquire(child_node, owner, NodeLock.LockType.READ, timeout);
+ lockTypeRequired = NodeLock.LockType.READ;
}
}
+ // reverse the "remove" if the node has been previously removed in the same tx, if this operation is a put()
+ if (gtx != null && needToReverseRemove(child_node, tx_table.get(gtx), lock_type, isDeleteOperation, createIfNotExists))
+ {
+ reverseRemove(child_node);
+ }
- if (acquired)
+ acquireNodeLock(child_node, owner, gtx, lockTypeRequired, timeout);
+
+ if (recursive && isTargetNode(i, treeNodeSize))
+ {
+ //Set acquired_locks=child_node.acquireAll(owner, lock_timeout, lock_type);
+ Set acquired_locks = lockManager.acquireAll(child_node, owner, lock_type, timeout);
+ if (acquired_locks.size() > 0)
{
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, lockManager.getLock(child_node));
+ cache.getTransactionTable().addLocks(gtx, acquired_locks);
}
else
{
- NodeLock l = lockManager.getLock(child_node);
List locks = getLocks(currentThread);
- if (!locks.contains(l))
+ locks.addAll(acquired_locks);
+ }
+ }
+ }
+ n = child_node;
+ }
+
+ // Add the Fqn to be removed to the transaction entry so we can clean up after ourselves during commit/rollback
+ if (isDeleteOperation && gtx != null) cache.getTransactionTable().get(gtx).addRemovedNode(fqn);
+ }
+
+ private boolean needToReverseRemove(Node n, TransactionEntry te, NodeLock.LockType lockTypeRequested, boolean isRemoveOperation, boolean createIfNotExists)
{
- locks.add(l);
+ return !isRemoveOperation && createIfNotExists && lockTypeRequested == NodeLock.LockType.WRITE && ((DataNode) n).isDeleted() && te.getRemovedNodes().contains(n.getFqn());
}
+
+ private void reverseRemove(Node n)
+ {
+ ((DataNode) n).markAsDeleted(false);
}
+
+ private boolean writeLockNeeded(NodeLock.LockType lock_type, int currentNodeIndex, int treeNodeSize, boolean isRemoveOperation, boolean createIfNotExists, Fqn targetFqn, Fqn currentFqn)
+ {
+ if (isRemoveOperation && currentNodeIndex == treeNodeSize - 2)
+ {
+ return true;// we're doing a remove and we've reached the PARENT node of the target to be removed.
}
- if (recursive && i == (treeNodeSize - 1))
+ if (!isTargetNode(currentNodeIndex, treeNodeSize) && !cache.hasChild(new Fqn(currentFqn, targetFqn.get(currentNodeIndex + 1))))
{
- //Set acquired_locks=child_node.acquireAll(owner, lock_timeout, lock_type);
- Set acquired_locks = lockManager.acquireAll(child_node, owner, lock_type, timeout);
- if (acquired_locks.size() > 0)
+ return createIfNotExists;// we're at a node in the tree, not yet at the target node, and we need to create the next node. So we need a WL here.
+ }
+
+ return lock_type == NodeLock.LockType.WRITE && isTargetNode(currentNodeIndex, treeNodeSize) && (createIfNotExists || isRemoveOperation);//normal operation, write lock explicitly requested and this is the target to be written to.
+ }
+
+ private boolean isTargetNode(int nodePosition, int treeNodeSize)
+ {
+ return nodePosition == (treeNodeSize - 1);
+ }
+
+ private void acquireNodeLock(Node node, Object owner, GlobalTransaction gtx, NodeLock.LockType lock_type, long lock_timeout) throws LockingException, TimeoutException, InterruptedException
+ {
+ boolean acquired = lockManager.acquire(node, owner, lock_type, lock_timeout);
+ if (acquired)
+ {
+ // Record the lock for release on method return or tx commit/rollback
+ recordNodeLock(gtx, node.getNodeSPI().getLock());
+ }
+ }
+
+ private void recordNodeLock(GlobalTransaction gtx, NodeLock lock)
{
if (gtx != null)
{
- cache.getTransactionTable().addLocks(gtx, acquired_locks);
+ // 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
{
+ Thread currentThread = Thread.currentThread();
List locks = getLocks(currentThread);
- locks.addAll(acquired_locks);
- }
- }
+ if (!locks.contains(lock))
+ {
+ locks.add(lock);
+ lock_table.put(currentThread, locks);
}
- n = child_node;
}
}
@@ -349,8 +426,15 @@
if (child_node == null)
{
cache.getInvocationContext().getOptionOverrides().setBypassInterceptorChain(true);
- child_node = n.addChild(childFqn); //, gtx, true);
+ child_node = n.addChild(childFqn);//, gtx, true);
+ }
+ // test if this node needs to be 'undeleted'
+ // reverse the "remove" if the node has been previously removed in the same tx, if this operation is a put()
+ if (gtx != null && needToReverseRemove(child_node, tx_table.get(gtx), NodeLock.LockType.WRITE, false, true))
+ {
+ reverseRemove(child_node);
}
+
if (child_node == null)
{
if (log.isTraceEnabled())
@@ -382,6 +466,15 @@
return;
}
+ // first remove nodes that should be deleted.
+ Iterator removedNodes = entry.getRemovedNodes().iterator();
+ TreeCacheProxyImpl tcpi = (TreeCacheProxyImpl) cache;
+ while (removedNodes.hasNext())
+ {
+ Fqn f = (Fqn) removedNodes.next();
+ tcpi.realRemove(f, false);
+ }
+
// Let's do it in stack style, LIFO
entry.releaseAllLocksLIFO(gtx);
@@ -421,6 +514,16 @@
return;
}
+
+ Iterator removedNodes = entry.getRemovedNodes().iterator();
+ TreeCacheProxyImpl tcpi = (TreeCacheProxyImpl) cache;
+ while (removedNodes.hasNext())
+ {
+ Fqn f = (Fqn) removedNodes.next();
+ tcpi.realRemove(f, false);
+
+ }
+
// 1. Revert the modifications by running the undo-op list in reverse. This *cannot* throw any exceptions !
entry.undoOperations(cache);
@@ -428,16 +531,16 @@
// put(/a/b/c) on /a, create b and c, plus undo operations _remove(a/b/c) and _remove(/a/b)
// 2. Remove all temporary nodes. Need to do it backwards since node is LIFO.
-// for(ListIterator it=new LinkedList(entry.getNodes()).listIterator(entry.getNodes().size());
-// it.hasPrevious();) {
-// node_name=(Fqn)it.previous();
-// try {
-// cache._remove(tx, node_name, false);
-// }
-// catch(Throwable t) {
-// log.error("failed removing node \"" + node_name + "\"", t);
-// }
-// }
+ // for(ListIterator it=new LinkedList(entry.getNodes()).listIterator(entry.getNodes().size());
+ // it.hasPrevious();) {
+ // node_name=(Fqn)it.previous();
+ // try {
+ // cache._remove(tx, node_name, false);
+ // }
+ // catch(Throwable t) {
+ // log.error("failed removing node \"" + node_name + "\"", t);
+ // }
+ // }
// 3. Finally, release all locks held by this TX
// Let's do it in stack style, LIFO
1.36 +7 -1 JBossCache/src/org/jboss/cache/interceptors/OptimisticNodeInterceptor.java
(In the diff below, changes in quantity of whitespace are not shown.)
Index: OptimisticNodeInterceptor.java
===================================================================
RCS file: /cvsroot/jboss/JBossCache/src/org/jboss/cache/interceptors/OptimisticNodeInterceptor.java,v
retrieving revision 1.35
retrieving revision 1.36
diff -u -b -r1.35 -r1.36
--- OptimisticNodeInterceptor.java 12 Dec 2006 14:51:44 -0000 1.35
+++ OptimisticNodeInterceptor.java 14 Dec 2006 17:18:48 -0000 1.36
@@ -94,13 +94,17 @@
workspaceNode.setVersion(version);
if (log.isTraceEnabled())
+ {
log.trace("Setting versioning for node " + workspaceNode.getFqn() + " to explicit");
+ }
workspaceNode.setVersioningImplicit(false);
}
else
{
if (log.isTraceEnabled())
+ {
log.trace("Setting versioning for node " + workspaceNode.getFqn() + " to implicit");
+ }
workspaceNode.setVersioningImplicit(true);
}
}
@@ -108,8 +112,10 @@
{
// "fail-more-silently" patch thanks to Owen Taylor - JBCACHE-767
if ((ctx.getOptionOverrides() == null || !ctx.getOptionOverrides().isFailSilently()) && MethodDeclarations.isPutMethod(m.getMethodId()))
+ {
throw new CacheException("Unable to set node version for " + getFqn(args) + ", node is null.");
}
+ }
switch (m.getMethodId())
{
@@ -316,7 +322,7 @@
if (toDelete.getFqn().isChildOrEquals(nodeFqn))
{
if (debug) log.debug("marking node " + toDelete.getFqn() + " as deleted");
- toDelete.markAsDeleted();
+ toDelete.markAsDeleted(true);
}
else
{
More information about the jboss-cvs-commits
mailing list