/**
*
*/
package vroom.common.utilities;
import java.util.LinkedList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import vroom.common.utilities.logging.LoggerHelper;
/**
* <code>ExtendedReentrantLock</code> is an extension of {@link ReentrantLock} providing additional monitoring functions.
* <p>
* Creation date: Jun 30, 2010 - 3:53:14 PM
*
* @author Victor Pillac, <a href="http://uniandes.edu.co">Universidad de Los Andes</a>-<a href="http://copa.uniandes.edu.co">Copa</a> <a
* href="http://www.emn.fr">Ecole des Mines de Nantes</a>-<a href="http://www.irccyn.ec-nantes.fr/irccyn/d/en/equipes/Slp">SLP</a>
* @version 1.0
*/
public class ExtendedReentrantLock extends ReentrantLock {
private static final long serialVersionUID = 1L;
/** set to <code>true</code> to enable detailed logging */
public static boolean sLoggingEnabled = false;
/** set to <code>true</code> to enable extended debugging */
public static boolean sRecordStack = false;
private long mLastLockTime = 0;
private final LinkedList<StackTraceElement[]> mLocksStack;
private StackTraceElement[] mLastStack;
/**
* Getter for <code>lastLockTime</code>
*
* @return the lastLockTime
*/
public long getLastLockTime() {
return mLastLockTime;
}
public static final LoggerHelper LOGGER = LoggerHelper.getLogger(ReentrantLock.class.getSimpleName());
/**
* Getter for the owner thread name
*
* @return
*/
public String getOwnerName() {
Thread owner = super.getOwner();
return owner != null ? owner.getName() : "none";
}
/**
* Creates a new <code>ExtendedReentrantLock</code>
*/
public ExtendedReentrantLock() {
mLocksStack = new LinkedList<StackTraceElement[]>();
}
/**
* Creates a new <code>ExtendedReentrantLock</code>
*
* @param fair
*/
public ExtendedReentrantLock(boolean fair) {
super(fair);
mLocksStack = new LinkedList<StackTraceElement[]>();
}
@Override
public void lockInterruptibly() throws InterruptedException {
super.lockInterruptibly();
if (sRecordStack) {
mLastLockTime = System.currentTimeMillis();
mLastStack = Thread.currentThread().getStackTrace();
mLocksStack.add(mLastStack);
}
}
/*
* (non-Javadoc)
* @see java.util.concurrent.locks.ReentrantLock#lock()
*/
@Override
public void lock() {
super.lock();
if (sRecordStack) {
mLastLockTime = System.currentTimeMillis();
mLastStack = Thread.currentThread().getStackTrace();
mLocksStack.add(mLastStack);
}
if (sLoggingEnabled) {
LOGGER.lowDebug("%s acquired lock %s", Thread.currentThread(), hashCode());
}
}
/*
* (non-Javadoc)
* @see java.util.concurrent.locks.ReentrantLock#tryLock(long, java.util.concurrent.TimeUnit)
*/
@Override
public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
boolean b = true;
b = super.tryLock(timeout, unit);
if (b && sRecordStack) {
mLastLockTime = System.currentTimeMillis();
mLastStack = Thread.currentThread().getStackTrace();
mLocksStack.add(mLastStack);
}
if (sLoggingEnabled) {
if (b) {
LOGGER.lowDebug("%s acquired lock %s", Thread.currentThread(), hashCode());
} else {
LOGGER.lowDebug("%s failed to acquired lock %s", Thread.currentThread(), hashCode());
}
}
return b;
}
/*
* (non-Javadoc)
* @see java.util.concurrent.locks.ReentrantLock#unlock()
*/
@Override
public void unlock() {
if (sRecordStack && mLastStack != null) {
StackTraceElement[] currentStack = Thread.currentThread().getStackTrace();
for (int i = 1; i < mLastStack.length; i++) {
if (i > currentStack.length) {
throw new IllegalStateException("Lock and Unlock were not perfomed in the same method");
}
StackTraceElement l = mLastStack[i];
if (!l.getMethodName().contains("lock") && !l.getMethodName().contains("Lock")) {
StackTraceElement u = currentStack[i];
if (!l.getClassName().equalsIgnoreCase(u.getClassName())
|| !l.getMethodName().equalsIgnoreCase(u.getMethodName())) {
throw new IllegalStateException(
"Lock and Unlock were not perfomed in the same method");
}
}
}
mLocksStack.pollLast();
mLastStack = mLocksStack.isEmpty() ? null : mLocksStack.getLast();
}
super.unlock();
if (sRecordStack && getHoldCount() == 0) {
mLastLockTime = -1;
mLocksStack.clear();
mLastStack = null;
}
if (sLoggingEnabled) {
LOGGER.lowDebug("%s released lock %s", Thread.currentThread(), hashCode());
}
}
@Override
public String toString() {
StringBuilder b = new StringBuilder(mLocksStack.size() * 100);
int i = 0;
for (StackTraceElement[] stack : mLocksStack) {
b.append("\nLock " + (i++) + " ");
for (int j = 1; j < stack.length; j++) {
if (j > 1) {
b.append("\n ");
}
b.append(String.format("%s.%s (%s %s)", stack[j].getClassName(), stack[j].getMethodName(),
stack[j].getFileName(), stack[j].getLineNumber()));
}
}
if (mLocksStack.isEmpty()) {
b.append("NA");
}
return String.format("Lock %s owned by %s (%s holds) for %sms - Stacks: %s", hashCode(), getOwner(),
getHoldCount(), getLastLockTime() >= 0 ? System.currentTimeMillis() - getLastLockTime() : "NA", b);
}
}