Clover coverage report -
Coverage timestamp: Thu Jul 5 2007 20:02:32 EDT
file stats: LOC: 596   Methods: 27
NCLOC: 412   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
IdentityLock.java 73.3% 84.4% 92.6% 81.8%
coverage coverage
 1    /*
 2    * JBoss, the OpenSource J2EE webOS
 3    *
 4    * Distributable under LGPL license.
 5    * See terms of license at gnu.org.
 6    */
 7    package org.jboss.cache.lock;
 8   
 9    import org.apache.commons.logging.Log;
 10    import org.apache.commons.logging.LogFactory;
 11    import org.jboss.cache.Fqn;
 12    import org.jboss.cache.Node;
 13    import org.jboss.cache.NodeSPI;
 14   
 15    import java.util.ArrayList;
 16    import java.util.Collection;
 17    import java.util.Collections;
 18    import java.util.HashSet;
 19    import java.util.Iterator;
 20    import java.util.Set;
 21    import java.util.concurrent.TimeUnit;
 22    import java.util.concurrent.locks.Lock;
 23   
 24    /**
 25    * Lock object which grants and releases locks, and associates locks with
 26    * <em>owners</em>. The methods to acquire and release a lock require an owner
 27    * (Object). When a lock is acquired, we store the current owner with the lock.
 28    * When the same owner (<em>but possibly running in a different thread</em>)
 29    * wants to acquire the lock, it is immediately granted. When an owner
 30    * different from the one currently owning the lock wants to release the lock,
 31    * we do nothing (no-op).
 32    * <p/>
 33    * Consider the following example:
 34    * <pre>
 35    * FIFOSemaphore lock=new FIFOSemaphore(1);
 36    * lock.acquire();
 37    * lock.acquire();
 38    * lock.release();
 39    * </pre>
 40    * This would block on the second <tt>acquire()</tt> (although we currently already hold
 41    * the lock) because the lock only has 1 permit. So <tt>IdentityLock</tt> will allow the
 42    * following code to work properly:
 43    * <pre>
 44    * IdentityLock lock=new IdentityLock();
 45    * lock.readLock().acquire(this, timeout);
 46    * lock.writeLock().acquire(this, timeout);
 47    * lock.release(this);
 48    * </pre>
 49    * If the owner is null, then the current thread is taken by default. This allow the following
 50    * code to work:
 51    * <pre>
 52    * IdentityLock lock=new IdentityLock();
 53    * lock.readLock().attempt(null, timeout);
 54    * lock.writeLock().attempt(null, timeout);
 55    * lock.release(null);
 56    * </pre>
 57    * <br/>
 58    * Note that the Object given as owner is required to implement {@link Object#equals}. For
 59    * a use case see the examples in the testsuite.
 60    *
 61    * @author <a href="mailto:bela@jboss.org">Bela Ban</a> Apr 11, 2003
 62    * @author Ben Wang July 2003
 63    * @version $Revision: 1.25 $
 64    */
 65    public class IdentityLock implements NodeLock
 66    {
 67   
 68    /**
 69    * Initialized property for debugging "print_lock_details"
 70    */
 71    private boolean PRINT_LOCK_DETAILS = Boolean.getBoolean("print_lock_details");
 72   
 73    private static final Log log = LogFactory.getLog(IdentityLock.class);
 74    private static boolean trace = log.isTraceEnabled();
 75    private final LockStrategy lock_;
 76    private final LockMap map_;
 77    private final boolean mustReacquireRead_;
 78    private NodeSPI<?, ?> node;
 79   
 80    /**
 81    * Creates a new Identity lock based on the lock level set in the {@link LockStrategyFactory}
 82    */
 83  24 public IdentityLock(NodeSPI node)
 84    {
 85  24 this(LockStrategyFactory.getLockStrategy(), node);
 86  24 log.trace("Using default lock level");
 87    }
 88   
 89    /**
 90    * Creates a new IdentityLock based on the lock level in force.
 91    *
 92    * @param level
 93    */
 94  180118 public IdentityLock(IsolationLevel level, NodeSPI node)
 95    {
 96  180118 this(LockStrategyFactory.getLockStrategy(level), node);
 97    }
 98   
 99  180142 private IdentityLock(LockStrategy strategy, NodeSPI node)
 100    {
 101  180142 lock_ = strategy;
 102  180142 mustReacquireRead_ = strategy instanceof LockStrategyReadCommitted;
 103  180142 map_ = new LockMap();
 104  180142 this.node = node;
 105    }
 106   
 107    /**
 108    * Returns the node for this lock, may be <code>null</code>.
 109    */
 110  0 public Node getNode()
 111    {
 112  0 return node;
 113    }
 114   
 115    /**
 116    * Returns the FQN this lock, may be <code>null</code>.
 117    */
 118  158 public Fqn getFqn()
 119    {
 120  158 if (node == null)
 121    {
 122  0 return null;
 123    }
 124  158 return node.getFqn();
 125    }
 126   
 127    /**
 128    * Return a copy of the reader lock owner in List. Size is zero is not available. Note that this list
 129    * is synchronized.
 130    *
 131    * @return Set of readers
 132    */
 133  26278 public Set getReaderOwners()
 134    {
 135  26278 return map_.readerOwners();
 136    }
 137   
 138    /**
 139    * Return the writer lock owner object. Null if not available.
 140    *
 141    * @return Object owner
 142    */
 143  26280 public Object getWriterOwner()
 144    {
 145  26277 return map_.writerOwner();
 146    }
 147   
 148    /**
 149    * Acquire a write lock with a timeout of <code>timeout</code> milliseconds.
 150    * Note that if the current owner owns a read lock, it will be upgraded
 151    * automatically. However, if upgrade fails, i.e., timeout, the read lock will
 152    * be released automatically.
 153    *
 154    * @param caller Can't be null.
 155    * @param timeout
 156    * @return boolean True if lock was acquired and was not held before, false if lock was held
 157    * @throws LockingException
 158    * @throws TimeoutException
 159    */
 160  541994 public boolean acquireWriteLock(Object caller, long timeout) throws LockingException, TimeoutException, InterruptedException
 161    {
 162  541994 if (trace)
 163    {
 164  0 log.trace(new StringBuffer("acquiring WL: fqn=").append(getFqn()).append(", caller=").append(caller).
 165    append(", lock=").append(toString(PRINT_LOCK_DETAILS)));
 166    }
 167  541994 boolean flag = acquireWriteLock0(caller, timeout);
 168  541968 if (trace)
 169    {
 170  0 log.trace(new StringBuffer("acquired WL: fqn=").append(getFqn()).append(", caller=").append(caller).
 171    append(", lock=").append(toString(PRINT_LOCK_DETAILS)));
 172    }
 173  541968 return flag;
 174    }
 175   
 176  541994 private boolean acquireWriteLock0(Object caller, long timeout) throws LockingException, TimeoutException, InterruptedException
 177    {
 178  541994 if (caller == null)
 179    {
 180  0 throw new IllegalArgumentException("acquireWriteLock(): null caller");
 181    }
 182   
 183  541994 if (map_.isOwner(caller, LockMap.OWNER_WRITE))
 184    {
 185  85714 if (trace)
 186    {
 187  0 log.trace("acquireWriteLock(): caller already owns lock for " + getFqn() + " (caller=" + caller + ')');
 188    }
 189  85714 return false;// owner already has the lock
 190    }
 191   
 192    // Check first if we need to upgrade
 193  456280 if (map_.isOwner(caller, LockMap.OWNER_READ))
 194    {
 195    // Currently is a reader owner. Obtain the writer ownership then.
 196  14008 Lock wLock;
 197  14008 try
 198    {
 199  14008 if (trace)
 200    {
 201  0 log.trace("upgrading RL to WL for " + caller + ", timeout=" + timeout + ", locks: " + map_.printInfo());
 202    }
 203  14008 wLock = lock_.upgradeLockAttempt(timeout);
 204    }
 205    catch (UpgradeException ue)
 206    {
 207  1 String errStr = "acquireWriteLock(): lock upgrade failed for " + getFqn() + " (caller=" + caller + ", lock info: " + toString(true) + ')';
 208  1 log.trace(errStr, ue);
 209  1 throw new UpgradeException(errStr, ue);
 210    }
 211  14007 if (wLock == null)
 212    {
 213  1 release(caller);// bug fix: remember to release the read lock before throwing the exception
 214  1 map_.removeReader(caller);
 215  1 String errStr = "upgrade lock for " + getFqn() + " could not be acquired after " + timeout + " ms." +
 216    " Lock map ownership " + map_.printInfo() + " (caller=" + caller + ", lock info: " + toString(true) + ')';
 217  1 log.trace(errStr);
 218  1 throw new UpgradeException(errStr);
 219    }
 220  14006 try
 221    {
 222  14006 if (trace)
 223    {
 224  0 log.trace("upgrading lock for " + getFqn());
 225    }
 226  14006 map_.upgrade(caller);
 227    }
 228    catch (OwnerNotExistedException ex)
 229    {
 230  0 throw new UpgradeException("Can't upgrade lock to WL for " + getFqn() + ", error in LockMap.upgrade()", ex);
 231    }
 232    }
 233    else
 234    {
 235    // Not a current reader owner. Obtain the writer ownership then.
 236  442272 boolean rc = lock_.writeLock().tryLock(timeout, TimeUnit.MILLISECONDS);
 237   
 238    // we don't need to synchronize from here on because we own the semaphore
 239  442272 if (!rc)
 240    {
 241  24 String errStr = "write lock for " + getFqn() + " could not be acquired after " + timeout + " ms. " +
 242    "Locks: " + map_.printInfo() + " (caller=" + caller + ", lock info: " + toString(true) + ')';
 243  24 log.trace(errStr);
 244  24 throw new TimeoutException(errStr);
 245    }
 246  442248 map_.setWriterIfNotNull(caller);
 247    }
 248  456254 return true;
 249    }
 250   
 251    /**
 252    * Acquire a read lock with a timeout period of <code>timeout</code> milliseconds.
 253    *
 254    * @param caller Can't be null.
 255    * @param timeout
 256    * @return boolean True if lock was acquired and was not held before, false if lock was held
 257    * @throws LockingException
 258    * @throws TimeoutException
 259    */
 260  7971537 public boolean acquireReadLock(Object caller, long timeout) throws LockingException, TimeoutException, InterruptedException
 261    {
 262  7971537 if (trace)
 263    {
 264  0 log.trace(new StringBuffer("acquiring RL: fqn=").append(getFqn()).append(", caller=").append(caller).
 265    append(", lock=").append(toString(PRINT_LOCK_DETAILS)));
 266    }
 267  7970719 boolean flag = acquireReadLock0(caller, timeout);
 268  7971484 if (trace)
 269    {
 270  0 log.trace(new StringBuffer("acquired RL: fqn=").append(getFqn()).append(", caller=").append(caller).
 271    append(", lock=").append(toString(PRINT_LOCK_DETAILS)));
 272    }
 273  7971484 return flag;
 274    }
 275   
 276  7971537 private boolean acquireReadLock0(Object caller, long timeout)
 277    throws LockingException, TimeoutException, InterruptedException
 278    {
 279  7971529 boolean rc;
 280   
 281  7971537 if (caller == null)
 282    {
 283  0 throw new IllegalArgumentException("owner is null");
 284    }
 285   
 286  7971537 boolean hasRead = false;
 287  7971537 boolean hasRequired = false;
 288  7971537 if (mustReacquireRead_)
 289    {
 290  176 hasRequired = map_.isOwner(caller, LockMap.OWNER_WRITE);
 291  176 if (!hasRequired)
 292    {
 293  174 hasRead = map_.isOwner(caller, LockMap.OWNER_READ);
 294    }
 295    }
 296  7971361 else if (map_.isOwner(caller, LockMap.OWNER_ANY))
 297    {
 298  1535194 hasRequired = true;
 299    }
 300   
 301  7971537 if (hasRequired)
 302    {
 303  1535196 if (trace)
 304    {
 305  0 StringBuffer sb = new StringBuffer(64);
 306  0 sb.append("acquireReadLock(): caller ").append(caller).append(" already owns lock for ").append(getFqn());
 307  0 log.trace(sb.toString());
 308    }
 309  1535196 return false;// owner already has the lock
 310    }
 311   
 312  6436341 rc = lock_.readLock().tryLock(timeout, TimeUnit.MILLISECONDS);
 313   
 314    // we don't need to synchronize from here on because we own the semaphore
 315  6436341 if (!rc)
 316    {
 317  53 StringBuffer sb = new StringBuffer();
 318  53 sb.append("read lock for ").append(getFqn()).append(" could not be acquired by ").append(caller);
 319  53 sb.append(" after ").append(timeout).append(" ms. " + "Locks: ").append(map_.printInfo());
 320  53 sb.append(", lock info: ").append(toString(true));
 321  53 String errMsg = sb.toString();
 322  53 log.trace(errMsg);
 323  53 throw new TimeoutException(errMsg);
 324    }
 325   
 326    // Only add to the map if we didn't already have the lock
 327  6436284 if (!hasRead)
 328    {
 329  6436238 map_.addReader(caller);// this is synchronized internally, we don't need to synchronize here
 330    }
 331  6436288 return true;
 332    }
 333   
 334    /**
 335    * Release the lock held by the owner.
 336    *
 337    * @param caller Can't be null.
 338    */
 339  6894514 public void release(Object caller)
 340    {
 341  6894514 if (caller == null)
 342    {
 343  0 throw new IllegalArgumentException("IdentityLock.release(): null owner object.");
 344    }
 345   
 346    // Check whether to release reader or writer lock.
 347  6894514 if (map_.isOwner(caller, LockMap.OWNER_READ))
 348    {
 349  6422111 map_.removeReader(caller);
 350  6422111 lock_.readLock().unlock();
 351    }
 352  472403 else if (map_.isOwner(caller, LockMap.OWNER_WRITE))
 353    {
 354  455814 map_.removeWriter();
 355  455814 lock_.writeLock().unlock();
 356    }
 357    }
 358   
 359    /**
 360    * Release all locks associated with this instance.
 361    */
 362  291 public void releaseAll()
 363    {
 364  291 try
 365    {
 366  291 if ((map_.writerOwner()) != null)
 367    {
 368    // lock_.readLock().release();
 369  277 lock_.writeLock().unlock();
 370    }
 371   
 372  291 map_.releaseReaderOwners(lock_);
 373    }
 374    finally
 375    {
 376  291 map_.removeAll();
 377    }
 378    }
 379   
 380    /**
 381    * Same as releaseAll now.
 382    */
 383  0 public void releaseForce()
 384    {
 385  0 releaseAll();
 386    }
 387   
 388    /**
 389    * Check if there is a read lock.
 390    */
 391  225564 public boolean isReadLocked()
 392    {
 393  225564 return map_.isReadLocked();
 394    }
 395   
 396    /**
 397    * Check if there is a write lock.
 398    */
 399  225222 public boolean isWriteLocked()
 400    {
 401  225222 return map_.writerOwner() != null;
 402    }
 403   
 404    /**
 405    * Check if there is a read or write lock
 406    */
 407  225449 public boolean isLocked()
 408    {
 409  225449 return isReadLocked() || isWriteLocked();
 410    }
 411   
 412    /**
 413    * Am I a lock owner?
 414    *
 415    * @param o
 416    */
 417  67 public boolean isOwner(Object o)
 418    {
 419  67 return map_.isOwner(o, LockMap.OWNER_ANY);
 420    }
 421   
 422  54 public String toString()
 423    {
 424  54 return toString(false);
 425    }
 426   
 427  212 public String toString(boolean print_lock_details)
 428    {
 429  212 StringBuffer sb = new StringBuffer();
 430  212 toString(sb, print_lock_details);
 431  212 return sb.toString();
 432    }
 433   
 434  227 public void toString(StringBuffer sb)
 435    {
 436  227 toString(sb, false);
 437    }
 438   
 439  439 public void toString(StringBuffer sb, boolean print_lock_details)
 440    {
 441  439 boolean printed_read_owners = false;
 442  439 Collection read_owners = lock_ != null ? getReaderOwners() : null;
 443  439 if (read_owners != null && read_owners.size() > 0)
 444    {
 445    // Fix for JBCACHE-310 -- can't just call new ArrayList(read_owners) :(
 446    // Creating the ArrayList and calling addAll doesn't work either
 447    // Looking at the details of how this is implemented vs. the 2 prev
 448    // options, this doesn't look like it should be slower
 449  247 Iterator iter = read_owners.iterator();
 450  247 read_owners = new ArrayList(read_owners.size());
 451  247 while (iter.hasNext())
 452    {
 453  263 read_owners.add(iter.next());
 454    }
 455   
 456  247 sb.append("read owners=").append(read_owners);
 457  247 printed_read_owners = true;
 458    }
 459    else
 460    {
 461  192 read_owners = null;
 462    }
 463   
 464  439 Object write_owner = lock_ != null ? getWriterOwner() : null;
 465  439 if (write_owner != null)
 466    {
 467  192 if (printed_read_owners)
 468    {
 469  0 sb.append(", ");
 470    }
 471  192 sb.append("write owner=").append(write_owner);
 472    }
 473  439 if (read_owners == null && write_owner == null)
 474    {
 475  0 sb.append("<unlocked>");
 476    }
 477  439 if (print_lock_details)
 478    {
 479  158 sb.append(" (").append(lock_.toString()).append(')');
 480    }
 481    }
 482   
 483  8513504 public boolean acquire(Object caller, long timeout, NodeLock.LockType lock_type) throws LockingException, TimeoutException, InterruptedException
 484    {
 485  8513504 try
 486    {
 487  8513504 if (lock_type == NodeLock.LockType.NONE)
 488    {
 489  0 return true;
 490    }
 491  8513504 else if (lock_type == NodeLock.LockType.READ)
 492    {
 493  7971459 return acquireReadLock(caller, timeout);
 494    }
 495    else
 496    {
 497  541981 return acquireWriteLock(caller, timeout);
 498    }
 499    }
 500    catch (UpgradeException e)
 501    {
 502  2 StringBuffer buf = new StringBuffer("failure upgrading lock: fqn=").append(getFqn()).append(", caller=").append(caller).
 503    append(", lock=").append(toString(true));
 504  2 if (trace)
 505    {
 506  0 log.trace(buf.toString());
 507    }
 508  2 throw new UpgradeException(buf.toString(), e);
 509    }
 510    catch (LockingException e)
 511    {
 512  0 StringBuffer buf = new StringBuffer("failure acquiring lock: fqn=").append(getFqn()).append(", caller=").append(caller).
 513    append(", lock=").append(toString(true));
 514  0 if (trace)
 515    {
 516  0 log.trace(buf.toString());
 517    }
 518  0 throw new LockingException(buf.toString(), e);
 519    }
 520    catch (TimeoutException e)
 521    {
 522  77 StringBuffer buf = new StringBuffer("failure acquiring lock: fqn=").append(getFqn()).append(", caller=").append(caller).
 523    append(", lock=").append(toString(true));
 524  77 if (trace)
 525    {
 526  0 log.trace(buf.toString());
 527    }
 528  77 throw new TimeoutException(buf.toString(), e);
 529    }
 530    }
 531   
 532  30242 public Set<NodeLock> acquireAll(Object caller, long timeout, LockType lock_type)
 533    throws LockingException, TimeoutException, InterruptedException
 534    {
 535  30242 boolean acquired;
 536   
 537  30242 if (lock_type == LockType.NONE)
 538    {
 539  0 return Collections.emptySet();
 540    }
 541   
 542  30242 Set<NodeLock> retval = new HashSet<NodeLock>();
 543  30242 acquired = acquire(caller, timeout, lock_type);
 544  30229 if (acquired)
 545    {
 546  15647 retval.add(this);
 547    }
 548   
 549  30229 for (NodeSPI n : node.getChildrenDirect())
 550    {
 551  14280 retval.addAll(n.getLock().acquireAll(caller, timeout, lock_type));
 552    }
 553  30215 return retval;
 554    }
 555   
 556  28639 public void releaseAll(Object owner)
 557    {
 558  28639 for (NodeSPI n : node.getChildrenDirect())
 559    {
 560  23848 n.getLock().releaseAll(owner);
 561    }
 562  28639 release(owner);
 563    }
 564   
 565  1712 private void printIndent(StringBuffer sb, int indent)
 566    {
 567  1712 if (sb != null)
 568    {
 569  1712 for (int i = 0; i < indent; i++)
 570    {
 571  9540 sb.append(" ");
 572    }
 573    }
 574    }
 575   
 576  1712 public void printLockInfo(StringBuffer sb, int indent)
 577    {
 578  1712 boolean locked = isLocked();
 579   
 580  1712 printIndent(sb, indent);
 581  1712 sb.append(Fqn.SEPARATOR).append(node.getFqn().getLastElement());
 582  1712 if (locked)
 583    {
 584  227 sb.append("\t(");
 585  227 toString(sb);
 586  227 sb.append(")");
 587    }
 588   
 589  1712 for (NodeSPI n : node.getChildrenDirect())
 590    {
 591  1190 sb.append("\n");
 592  1190 n.getLock().printLockInfo(sb, indent + 4);
 593    }
 594    }
 595   
 596    }