From: Bela B. <be...@us...> - 2004-12-18 23:49:19
|
User: belaban Date: 04/12/18 15:49:12 Modified: src/main/org/jboss/cache/interceptors LockInterceptor.java Log: Fix for JBCACHE-17 (Jira): creation of node on-demand and locking it has to be done atomically, otherwise some other thread could remove the node *before* it is locked Revision Changes Path 1.9 +71 -19 jboss-cache/src/main/org/jboss/cache/interceptors/LockInterceptor.java Index: LockInterceptor.java =================================================================== RCS file: /cvsroot/jboss/jboss-cache/src/main/org/jboss/cache/interceptors/LockInterceptor.java,v retrieving revision 1.8 retrieving revision 1.9 diff -u -r1.8 -r1.9 --- LockInterceptor.java 8 Dec 2004 10:22:04 -0000 1.8 +++ LockInterceptor.java 18 Dec 2004 23:49:11 -0000 1.9 @@ -1,15 +1,16 @@ package org.jboss.cache.interceptors; +import EDU.oswego.cs.dl.util.concurrent.ReentrantLock; import org.jboss.cache.*; import org.jboss.cache.lock.IdentityLock; import org.jboss.cache.lock.LockingException; import org.jboss.cache.lock.TimeoutException; import org.jgroups.blocks.MethodCall; +import javax.transaction.Status; import javax.transaction.Synchronization; import javax.transaction.Transaction; import javax.transaction.TransactionManager; -import javax.transaction.Status; import java.lang.reflect.Method; import java.util.*; @@ -18,7 +19,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: LockInterceptor.java,v 1.8 2004/12/08 10:22:04 belaban Exp $ + * @version $Id: LockInterceptor.java,v 1.9 2004/12/18 23:49:11 belaban Exp $ */ public class LockInterceptor extends Interceptor { private TransactionManager tx_mgr=null; @@ -28,7 +29,6 @@ /** List<Transaction> that we have registered for */ private List transactions=Collections.synchronizedList(new ArrayList()); - final static int NONE = 0; final static int READ = 1; final static int WRITE = 2; @@ -51,6 +51,7 @@ int lock_type=NONE; Method meth=m.getMethod(); Object[] args=m.getArgs(); + boolean create_if_not_exists=false; /** List<IdentityLock> locks. Locks acquired during the current method; will be released on method return. * This list is only populated when there is no TX, otherwise the TransactionTable maintains the locks @@ -87,6 +88,7 @@ meth.equals(TreeCache.putKeyValMethodLocal)) { fqn=(Fqn)args[1]; lock_type=WRITE; + create_if_not_exists=true; } else if(meth.equals(TreeCache.removeNodeMethodLocal)) { fqn=(Fqn)args[1]; @@ -122,16 +124,12 @@ lock_type=READ; } - if(lock_type == NONE) { - return super.invoke(m); - } - try { - // 2. Lock the node (must be either read or write if we get here) + // Lock the node (must be either read or write if we get here) // If no TX: add each acquired lock to the list of locks for this method (locks) // If TX: [merge code from TransactionInterceptor]: register with TxManager, on commit/rollback, // release the locks for the given TX - lock(fqn, gtx, lock_type, locks); + lock(fqn, gtx, lock_type, locks, create_if_not_exists); retval=super.invoke(m); } finally { @@ -142,22 +140,39 @@ - - - /** * Locks a given node. * @param fqn * @param gtx - * @param lock_type Guaranteed to be READ or WRITE and *nothing else* + * @param lock_type READ, WRITE or NONE + * @param create_if_not_exists Create the node (and all parent nodes) if it doesn't exist */ - private void lock(Fqn fqn, GlobalTransaction gtx, int lock_type, LinkedList locks) throws TimeoutException, LockingException { + private void lock(Fqn fqn, GlobalTransaction gtx, int lock_type, LinkedList locks, boolean create_if_not_exists) + throws TimeoutException, LockingException { + final ReentrantLock create_lock=new ReentrantLock(); + + try { + create_lock.acquire(); + _lock(fqn, gtx, lock_type, locks, create_if_not_exists, create_lock); + } + catch(InterruptedException e) { + } + finally { + if(create_lock.holds() > 0) + create_lock.release(); + } + } + + + private void _lock(Fqn fqn, GlobalTransaction gtx, int lock_type, LinkedList locks, boolean create_if_not_exists, + ReentrantLock lock) + throws TimeoutException, LockingException { Node n, child_node=null; Object child_name; Fqn tmp_fqn=new Fqn(); int treeNodeSize; Object owner=gtx != null? gtx : (Object)Thread.currentThread(); - boolean acquired; + boolean acquired=false; if(fqn == null) { log.error("fqn is null - this should not be the case"); @@ -168,11 +183,16 @@ return; n=cache.getRoot(); + if(create_if_not_exists && !cache.exists(fqn)) { + createNode(fqn, gtx); // here, lock will be released after this method returns + } + else + lock.release(); + for(int i=0; i < treeNodeSize; i++) { child_name=fqn.get(i); tmp_fqn=new Fqn(tmp_fqn, child_name); child_node=n.getChild(child_name); - if(child_node == null) { if(log.isTraceEnabled()) log.trace("failed finding child " + child_name + " of node " + n.getFqn()); @@ -180,11 +200,16 @@ } // Write lock only for destination node in write request. - if(lock_type == WRITE && i == (treeNodeSize - 1)) { - acquired=child_node.acquire(owner, lock_acquisition_timeout, Node.LOCK_TYPE_WRITE); + if(lock_type == NONE) { + ; } else { - acquired=child_node.acquire(owner, lock_acquisition_timeout, Node.LOCK_TYPE_READ); + if(lock_type == WRITE && i == (treeNodeSize - 1)) { + acquired=child_node.acquire(owner, lock_acquisition_timeout, Node.LOCK_TYPE_WRITE); + } + else { + acquired=child_node.acquire(owner, lock_acquisition_timeout, Node.LOCK_TYPE_READ); + } } if(acquired) { @@ -204,6 +229,33 @@ } + private void createNode(Fqn fqn, GlobalTransaction tx) { + Node n, child_node=null; + Object child_name; + Fqn tmp_fqn=new Fqn(), copy; + + if(fqn == null) return; + int treeNodeSize=fqn.size(); + n=cache.getRoot(); + for(int i=0; i < treeNodeSize; i++) { + child_name=fqn.get(i); + tmp_fqn=new Fqn(tmp_fqn, child_name); + child_node=n.getChild(child_name); + if(child_node == null) { + copy=(Fqn)tmp_fqn.clone(); + child_node=n.createChild(child_name, copy, n); + if(tx != null) { + // add the node name to the list maintained for the current tx + // (needed for abort/rollback of transaction) + cache.addNode(tx, (Fqn)tmp_fqn.clone()); + } + cache.notifyNodeCreated(copy); + } + n=child_node; + } + } + + private void releaseLocks(LinkedList locks) { IdentityLock lock; |