Merge branch 'master' into racecommittee_integrated

Conflicts:
	java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/regattaoverview/FlagImageCell.java
This commit is contained in:
Armin Zamani committed 2013-04-22 18:34:40 +02:00
commit e6ddcadb36
290 files changed
+4605 -11342

No files matched your search

@@ -1,168 +1,359 @@
package com.sap.sailing.util.impl;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.logging.Logger;
import com.sap.sailing.domain.base.impl.MillisecondsTimePoint;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.Util;
/**
* Supports lock management for {@link NamedReentrantReadWriteLock} which is a specialization of
* {@link ReentrantReadWriteLock} that provides enhanced tracing capabilities. This class offers a number of utility
* methods which also support the propagation of locks from one thread to another. This can be used by threads to
* testify that they will wait for another thread which therefore doesn't need to acquire the same locks again. This can
* help to avoid deadlocks.
* <p>
*
* Locks are propagated from one thread to another in the form of counters managed in the {@link #propagationCounts} map that
* count, how many other threads have propagated their lock held. When a thread needs to obtain a lock, it first checks
* if this lock has a positive count in the map. If so, the lock doesn't need to actually be locked because some other
* thread has asserted that it holds the lock already. Instead, a counter is incremented in the {@link #virtualLockCounts}
* map that is used to monitor that the "virtual" locks are handled properly and that all of them have been unlocked
* when unpropagation happens.
* <p>
*
* Unlocking is a bit trickier for read locks because even if a thread A holds the read lock, a thread B may still
* obtain it. If A then propagates its locks to B, B needs to understand whether it last "obtained" the lock by just
* incrementing the counter on the propagated lock, or by actually obtaining the lock itself, and then perform the
* inverse operation. Note that lock propagation and unpropagation may happen more or less at any point in time
* (synchronized on the receiving thread's {@link #propagationCounts} entry), so thread B may first actually obtain the read
* lock, then receive propagated locks from thread A, then reentrantly lock again by incrementing the counter. The only
* guarantee that users of the {@link LockUtil} class need to provide is that the locks are unpropagated only after
* thread B is done with its locking operations as long as A waits for B. This in particular means that B is required to
* unlock all locks it obtained (actually or virtually by incrementing the counter) before unpropagation happens. Note
* also that more than one thread (A_1, A_2, ...) may propagate its locks to the same other thread B.
*
* @author Axel Uhl (d043530)
*
*/
public class LockUtil {
private enum ReadOrWrite { READ, WRITE };
private static final int NUMBER_OF_SECONDS_TO_WAIT_FOR_LOCK = 5;
private static final Logger logger = Logger.getLogger(Util.class.getName());
private static final Map<NamedReentrantReadWriteLock, TimePoint> lastTimeWriteLockWasObtained = new WeakHashMap<NamedReentrantReadWriteLock, TimePoint>();
private static final Map<Thread, Map<Lock, Integer>> lockCounts = new ConcurrentHashMap<Thread, Map<Lock,Integer>>();
/**
* Bug <a href="http://bugs.sun.com/view_bug.do?bug_id=6822370">http://bugs.sun.com/view_bug.do?bug_id=6822370</a> seems
* dangerous, particularly if it happens in a <code>LiveLeaderboardUpdater</code> thread. Even though the bug is reported to
* have been fixed in JDK 7(b79) we should be careful. This method tries to acquire a lock, allowing for five seconds to pass.
* After five seconds and not having retrieved the lock, tries again until the lock has been acquired.
* Tells how many other threads propagated which held lock to the key thread. During propagation, a lock is
* considered held if it is really locked by the propagating thread or if the propagating thread received it itself
* through propagation. The value maps only contain positive (non-zero) values. If a count goes to zero, the
* corresponding key lock is removed from the map.
* <p>
*
* The thread-specific value maps are used as monitor objects whenever decisions about thread-specific lock counts
* need to be made.
*/
private static void lock(Lock lock, String lockDescriptionForTimeoutLogMessage, NamedReentrantReadWriteLock lockParent) {
boolean locked = false;
boolean interrupted = false;
while (!locked) {
try {
locked = lock.tryLock(NUMBER_OF_SECONDS_TO_WAIT_FOR_LOCK, TimeUnit.SECONDS);
if (!locked) {
StringBuilder message = new StringBuilder();
message.append("Couldn't acquire lock ");
message.append(lockDescriptionForTimeoutLogMessage);
message.append(" in ");
message.append(NUMBER_OF_SECONDS_TO_WAIT_FOR_LOCK);
message.append("s in thread "+Thread.currentThread().getName()+" at ");
message.append(getCurrentStackTrace());
Thread writer = lockParent.getWriter();
if (writer != null) {
message.append("\nThe current writer is:\n");
appendThreadData(message, writer);
}
message.append("\nThe current readers are:\n");
for (Thread reader : lockParent.getReaders()) {
appendThreadData(message, reader);
}
message.append("Trying again...");
logger.info(message.toString());
}
}
catch (InterruptedException ex) {
interrupted = true;
}
}
if (interrupted) {
// re-assert interrupt state that occurred while we
// were acquiring the lock
Thread.currentThread().interrupt();
}
}
private static void appendThreadData(StringBuilder message, Thread writer) {
message.append(writer);
message.append('\n');
message.append(getStackTrace(writer));
message.append('\n');
}
private static final Map<Thread, Map<Lock, Integer>> propagationCounts = new ConcurrentHashMap<Thread, Map<Lock,Integer>>();
/**
* Counts the "virtual" locks. A "virtual" lock is obtained if and only if at the time of calling
* {@link #lockForRead(NamedReentrantReadWriteLock)} or {@link #lockForWrite(NamedReentrantReadWriteLock)} the
* respective lock has a positive {@link #propagationCounts propagation count} for the current thread.
*/
private static final Map<Thread, Map<Lock, Integer>> virtualLockCounts = new ConcurrentHashMap<Thread, Map<Lock, Integer>>();
/**
* Redundant but easily accessible hold count per thread and lock. These are the actual lock hold counts as they are
* recorded in the actual {@link NamedReentrantReadWriteLock} locks.
*/
private static final Map<Thread, Map<Lock, Integer>> lockCounts = new ConcurrentHashMap<Thread, Map<Lock, Integer>>();
public static void lockForRead(NamedReentrantReadWriteLock lock) {
if (isInCurrentThreadsLockSet(lock.readLock())) {
incrementLockCountForCurrentThread(lock.readLock());
} else {
lock(lock.readLock(), lock.getReadLockName(), lock);
addToCurrentThreadsLockSet(lock.readLock());
}
}
private static Map<Lock, Integer> getCurrentThreadsLockCounts() {
final Thread currentThread = Thread.currentThread();
return getLockCounts(currentThread);
acquireLockVirtuallyOrActually(lock, lock.readLock(), ReadOrWrite.READ);
}
private static Map<Lock, Integer> getLockCounts(final Thread thread) {
Map<Lock, Integer> result = lockCounts.get(thread);
if (result == null) {
result = new HashMap<Lock, Integer>();
lockCounts.put(thread, result);
}
return result;
}
private static void incrementLockCountForCurrentThread(Lock lock) {
Map<Lock, Integer> map = getCurrentThreadsLockCounts();
assert map.containsKey(lock);
map.put(lock, map.get(lock) + 1);
}
private static void decrementLockCountForCurrentThread(Lock lock) {
Map<Lock, Integer> map = getCurrentThreadsLockCounts();
assert map.containsKey(lock);
map.put(lock, map.get(lock)-1);
}
private static boolean isInCurrentThreadsLockSet(Lock lock) {
return getCurrentThreadsLockCounts().containsKey(lock);
}
private static void addToCurrentThreadsLockSet(Lock lock) {
getCurrentThreadsLockCounts().put(lock, 1);
}
private static void removeFromCurrentThreadsLockSet(Lock lock) {
getCurrentThreadsLockCounts().remove(lock);
}
public static void unlockAfterRead(NamedReentrantReadWriteLock lock) {
assert isInCurrentThreadsLockSet(lock.readLock());
if (getCurrentThreadsLockCounts().get(lock.readLock()) == 1) {
lock.readLock().unlock();
removeFromCurrentThreadsLockSet(lock.readLock());
} else {
decrementLockCountForCurrentThread(lock.readLock());
}
}
public static void lockForWrite(NamedReentrantReadWriteLock lock) {
if (isInCurrentThreadsLockSet(lock.writeLock())) {
incrementLockCountForCurrentThread(lock.writeLock());
} else {
lock(lock.writeLock(), lock.getWriteLockName(), lock);
addToCurrentThreadsLockSet(lock.writeLock());
synchronized (lastTimeWriteLockWasObtained) {
lastTimeWriteLockWasObtained.put(lock, MillisecondsTimePoint.now());
acquireLockVirtuallyOrActually(lock, lock.writeLock(), ReadOrWrite.WRITE);
synchronized (lastTimeWriteLockWasObtained) {
lastTimeWriteLockWasObtained.put(lock, MillisecondsTimePoint.now());
}
}
private static void acquireLockVirtuallyOrActually(NamedReentrantReadWriteLock lock, final Lock readOrWriteLock, final ReadOrWrite readOrWrite) {
boolean locked = false;
while (!locked) {
final Map<Lock, Integer> currentThreadsPropagationCounts = getCurrentThreadsPropagationCounts();
synchronized (currentThreadsPropagationCounts) {
if (currentThreadsPropagationCounts.containsKey(readOrWriteLock)) {
// read lock was propagated to current thread by at least one other thread; use virtual lock by incrementing counter:
incrementVirtualLockCountForCurrentThread(readOrWriteLock);
// Uncomment the following in case of issues you want to debug with logging:
// logger.finest("only incremented virtual count for "+readOrWrite+" lock " + lock.getName() + " in thread "
// + Thread.currentThread().getName()+" to "+getCurrentThreadsVirtualLockCounts().get(readOrWriteLock));
locked = true;
} else {
// lock was not yet propagated; try to actually obtain lock
locked = lock(readOrWriteLock, readOrWrite==ReadOrWrite.READ ? lock.getReadLockName() : lock.getWriteLockName(), lock);
if (locked) {
// Uncomment the following in case of issues you want to debug with logging:
// logger.finest("actually acquired "+readOrWrite+" lock " + lock.getName() + " in thread " + Thread.currentThread().getName());
incrementLockCountForCurrentThread(readOrWriteLock);
}
}
}
if (!locked) {
Thread.yield(); // let any other thread that is trying to propagate locks get access to currentThreadsPropagationCounts
}
}
}
public static void unlockAfterRead(NamedReentrantReadWriteLock lock) {
final ReadLock readOrWriteLock = lock.readLock();
unlockVirtuallyOrActually(lock, readOrWriteLock);
}
private static void unlockVirtuallyOrActually(NamedReentrantReadWriteLock lock, final Lock readOrWriteLock) {
Map<Lock, Integer> currentThreadPropagationCounts = getCurrentThreadsPropagationCounts();
synchronized (currentThreadPropagationCounts) {
assert isInCurrentThreadsLockSet(readOrWriteLock);
Integer virtualLockCount = getCurrentThreadsVirtualLockCounts().get(readOrWriteLock);
if (virtualLockCount != null) {
// an entry is a positive entry and means the current thread acquired the lock virtually; unlock virtually again
decrementVirtualLockCountForCurrentThread(readOrWriteLock);
// Uncomment the following in case of issues you want to debug with logging:
// logger.finest("only decremented virtual count for "+readOrWrite+" lock "+lock.getName()+" in thread "+Thread.currentThread().getName()+" from "+virtualLockCount);
} else {
// Uncomment the following in case of issues you want to debug with logging:
// logger.finest("actually unlocking "+readOrWrite+" lock "+lock.getName()+" in thread "+Thread.currentThread().getName());
readOrWriteLock.unlock();
decrementLockCountForCurrentThread(readOrWriteLock);
}
}
}
public static void unlockAfterWrite(NamedReentrantReadWriteLock lock) {
assert isInCurrentThreadsLockSet(lock.writeLock());
if (getCurrentThreadsLockCounts().get(lock.writeLock()) == 1) {
lock.writeLock().unlock();
removeFromCurrentThreadsLockSet(lock.writeLock());
final TimePoint timePointWriteLockWasObtained;
synchronized (lastTimeWriteLockWasObtained) {
timePointWriteLockWasObtained = lastTimeWriteLockWasObtained.get(lock);
Map<Lock, Integer> currentThreadPropagationCounts = getCurrentThreadsPropagationCounts();
synchronized (currentThreadPropagationCounts) {
unlockVirtuallyOrActually(lock, lock.writeLock());
}
final TimePoint timePointWriteLockWasObtained;
synchronized (lastTimeWriteLockWasObtained) {
timePointWriteLockWasObtained = lastTimeWriteLockWasObtained.get(lock);
}
if (timePointWriteLockWasObtained == null) {
logger.info("Internal error: write lock " + lock.getName()
+ " to be unlocked but no time recorded for when it was last obtained.\n"
+ "This is where the lock interaction happened:\n" + getCurrentStackTrace());
} else {
TimePoint now = MillisecondsTimePoint.now();
if (now.asMillis() - timePointWriteLockWasObtained.asMillis() > 10000l) {
String stackTrace = getCurrentStackTrace();
logger.info("write lock " + lock.getName() + " was held for more than 10s. It got unlocked here: "
+ stackTrace);
}
if (timePointWriteLockWasObtained == null) {
logger.info("Internal error: write lock "+lock.getName()+" to be unlocked but no time recorded for when it was last obtained.\n"+
"This is where the lock interaction happened:\n"+getCurrentStackTrace());
} else {
TimePoint now = MillisecondsTimePoint.now();
if (now.asMillis()-timePointWriteLockWasObtained.asMillis() > 10000l) {
String stackTrace = getCurrentStackTrace();
logger.info("write lock "+lock.getName()+" was held for more than 10s. It got unlocked here: "+
stackTrace);
}
}
/**
* ATTENTION: Calling this method makes a very strong assertion! It asserts that the calling thread will call
* {@link #unpropagateLockSetFrom(Thread)} for the same <code>from</code> thread passed to this call before
* <code>from</code> releases any of the locks it currently holds. The effect of making this call is that the
* calling thread, when trying to acquire a lock already held by <code>from</code>, will not actually acquire that
* lock again. This, in particular, has the effect that a read lock already held by <code>from</code> will not
* have to be acquired again, which in turn avoids a read-read deadlock on a <em>fair</em> lock in case another
* thread is attempting to acquire the corresponding write lock before the current thread tries to re-acquire the
* read lock.
* <p>
*
* Always use this in a <code>try/finally</code> combination where in the <code>finally</code> block you call
* {@link #unpropagateLockSetFrom(Thread)}.
*/
public static void propagateLockSetFrom(Thread from) {
Thread to = Thread.currentThread();
propagateLockSet(from, to);
}
/**
* Propagates all locks that <code>from</code> received itself through propagation and those that <code>from</code> actually
* locked itself. If <code>from</code> received a lock through propagation <em>and</em> actually holds it, it is only
* propagated once.
*/
private static void propagateLockSet(Thread from, Thread to) {
Set<Lock> locksToPropagate = getLocksHeldVirtuallyOrActuallyBy(from);
Map<Lock, Integer> toMap = getPropagationCounts(to);
synchronized (toMap) {
for (Lock lockToPropagate : locksToPropagate) {
increment(lockToPropagate, toMap);
}
}
}
/**
* ATTENTION: Calling this method makes a very strong assertion! It asserts that the calling thread will not call
* {@link #unpropagateLockSetTo(Thread)} for the same <code>to</code> thread passed to this call before the calling
* thread releases any of the locks it currently holds. The effect of making this call is that the <code>to</code>
* thread, when trying to acquire a lock already held by the calling thread, will not actually acquire that lock
* again. This, in particular, has the effect that a read lock already held by the calling thread will not have to
* be acquired again by <code>to</code>, which in turn avoids a read-read deadlock on a <em>fair</em> lock in case
* another thread is attempting to acquire the corresponding write lock before <code>to</code> tries to re-acquire
* the read lock.
* <p>
*
* Always use this in a <code>try/finally</code> combination where in the <code>finally</code> block you call
* {@link #unpropagateLockSetTo(Thread)}.
*/
public static void propagateLockSetTo(Thread to) {
Thread from = Thread.currentThread();
propagateLockSet(from, to);
}
/**
* A thread that previously propagated the lock set from another thread by using {@link #propagateLockSetFrom(Thread)}
* ends the propagation with this call. After this call, <code>thread</code> is free to release any locks it held
* at the time {@link #propagateLockSetFrom(Thread)} was called by the current thread with <code>thread</code> as
* the argument.
*/
public static void unpropagateLockSetFrom(Thread from) {
Thread to = Thread.currentThread();
unpropagateLockSet(from, to);
}
private static void unpropagateLockSet(Thread from, Thread to) {
Set<Lock> locksToPropagate = getLocksHeldVirtuallyOrActuallyBy(from);
Map<Lock, Integer> toMap = getPropagationCounts(to);
synchronized (toMap) {
for (Lock lockToPropagate : locksToPropagate) {
decrement(lockToPropagate, toMap);
}
}
}
private static Set<Lock> getLocksHeldVirtuallyOrActuallyBy(Thread thread) {
Set<Lock> locksToPropagate = new HashSet<Lock>();
Map<Lock, Integer> propagationMap = propagationCounts.get(thread);
if (propagationMap != null) {
// first synchronize fromMap, then toMap; this way, no deadlock can occur as long as propagation works in the same direction
synchronized (propagationMap) {
for (Map.Entry<Lock, Integer> otherEntry : propagationMap.entrySet()) {
locksToPropagate.add(otherEntry.getKey());
}
}
} else {
decrementLockCountForCurrentThread(lock.writeLock());
}
Map<Lock, Integer> lockMap = lockCounts.get(thread);
if (lockMap != null) {
for (Map.Entry<Lock, Integer> otherEntry : lockMap.entrySet()) {
locksToPropagate.add(otherEntry.getKey());
}
}
return locksToPropagate;
}
/**
* A thread that previously propagated the lock set to another thread by using {@link #propagateLockSetTo(Thread)}
* ends the propagation with this call. After this call, the current thread is free to release any locks it held
* at the time {@link #propagateLockSetTo(Thread)} was called by the current thread with <code>to</code> as
* the argument.
*/
public static void unpropagateLockSetTo(Thread to) {
Thread from = Thread.currentThread();
unpropagateLockSet(from, to);
}
private static Map<Lock, Integer> getCurrentThreadsPropagationCounts() {
final Thread currentThread = Thread.currentThread();
return getPropagationCounts(currentThread);
}
private static Map<Lock, Integer> getCurrentThreadsLockCounts() {
final Thread currentThread = Thread.currentThread();
return getLockCounts(currentThread);
}
private static Map<Lock, Integer> getCurrentThreadsVirtualLockCounts() {
final Thread currentThread = Thread.currentThread();
return getVirtualLockCounts(currentThread);
}
private static Map<Lock, Integer> getOrCreateMapForThread(final Thread thread, Map<Thread, Map<Lock, Integer>> map) {
// don't synchronize all the frequent read accesses
Map<Lock, Integer> result = map.get(thread);
if (result == null) {
// but if we need to create a new entry, ensure that this doesn't happen concurrently
synchronized (map) {
result = map.get(thread);
if (result == null) {
result = new HashMap<Lock, Integer>();
map.put(thread, result);
}
}
}
return result;
}
private static Map<Lock, Integer> getLockCounts(final Thread thread) {
return getOrCreateMapForThread(thread, lockCounts);
}
private static Map<Lock, Integer> getPropagationCounts(final Thread thread) {
return getOrCreateMapForThread(thread, propagationCounts);
}
private static Map<Lock, Integer> getVirtualLockCounts(final Thread thread) {
return getOrCreateMapForThread(thread, virtualLockCounts);
}
private static void incrementLockCountForCurrentThread(Lock lock) {
Map<Lock, Integer> map = getCurrentThreadsLockCounts();
increment(lock, map);
}
private static void decrementLockCountForCurrentThread(Lock lock) {
decrement(lock, getCurrentThreadsLockCounts());
}
private static void incrementVirtualLockCountForCurrentThread(Lock lock) {
increment(lock, getCurrentThreadsVirtualLockCounts());
}
private static void decrementVirtualLockCountForCurrentThread(Lock lock) {
decrement(lock, getCurrentThreadsVirtualLockCounts());
}
private static void increment(Lock lock, Map<Lock, Integer> map) {
final int newValue;
if (map.containsKey(lock)) {
newValue = map.get(lock) + 1;
} else {
newValue = 1;
}
map.put(lock, newValue);
}
private static void decrement(Lock lock, Map<Lock, Integer> map) {
assert map.containsKey(lock);
final int newValue = map.get(lock) - 1;
if (newValue == 0) {
map.remove(lock);
} else {
map.put(lock, newValue);
}
}
private static boolean isInCurrentThreadsLockSet(Lock lock) {
return getCurrentThreadsLockCounts().containsKey(lock) || getCurrentThreadsVirtualLockCounts().containsKey(lock);
}
private static String getStackTrace(Thread thread) {
StringBuilder sb = new StringBuilder();
for (StackTraceElement sf : thread.getStackTrace()) {
@@ -177,46 +368,50 @@ public class LockUtil {
}
/**
* ATTENTION: Calling this method makes a very strong assertion! It asserts that the calling thread
* will call {@link #unpropagateLockSetFrom(Thread)} for the same <code>thread</code> passed to this
* call before <code>thread</code> releases any of the locks it currently holds. The effect of making
* this call is that the calling thread, when trying to acquire a lock already held by <code>thread</code>,
* will not actually acquire that lock again. This, in particular, has the effect that a read lock
* already held by <code>thread</code> will not have to be acquired again, which in turn avoids a
* read-read deadlock on a fair lock in case another thread is attempting to acquire the corresponding
* write lock before the current thread tries to re-acquire the read lock.<p>
* Actually obtains read or write lock <code>lock</code> of the {@link NamedReentrantReadWriteLock} owner lock.<p>
*
* Always use this in a <code>try/finally</code> combination where in the <code>finally</code> you call
* {@link #unpropagateLockSetFrom(Thread)}.
* Bug <a href="http://bugs.sun.com/view_bug.do?bug_id=6822370">http://bugs.sun.com/view_bug.do?bug_id=6822370</a> seems
* dangerous, particularly if it happens in a <code>LiveLeaderboardUpdater</code> thread. Even though the bug is reported to
* have been fixed in JDK 7(b79) we should be careful. This method tries to acquire a lock, allowing for five seconds to pass.
* After five seconds and not having retrieved the lock, tries again until the lock has been acquired.
*/
public static void propagateLockSetFrom(Thread thread) {
Map<Lock, Integer> otherMap = lockCounts.get(thread);
if (otherMap != null) {
Map<Lock, Integer> currentMap = getCurrentThreadsLockCounts();
for (Map.Entry<Lock, Integer> otherEntry : otherMap.entrySet()) {
if (currentMap.containsKey(otherEntry.getKey())) {
currentMap.put(otherEntry.getKey(), currentMap.get(otherEntry.getKey()) + otherEntry.getValue());
} else {
currentMap.put(otherEntry.getKey(), otherEntry.getValue());
private static boolean lock(Lock lock, String lockDescriptionForTimeoutLogMessage, NamedReentrantReadWriteLock lockParent) {
boolean locked = false;
try {
locked = lock.tryLock(NUMBER_OF_SECONDS_TO_WAIT_FOR_LOCK, TimeUnit.SECONDS);
if (!locked) {
StringBuilder message = new StringBuilder();
message.append("Couldn't acquire lock ");
message.append(lockDescriptionForTimeoutLogMessage);
message.append(" in ");
message.append(NUMBER_OF_SECONDS_TO_WAIT_FOR_LOCK);
message.append("s in thread " + Thread.currentThread().getName() + " at ");
message.append(getCurrentStackTrace());
Thread writer = lockParent.getWriter();
if (writer != null) {
message.append("\nThe current writer is:\n");
appendThreadData(message, writer);
}
message.append("\nThe current readers are:\n");
for (Thread reader : lockParent.getReaders()) {
appendThreadData(message, reader);
}
message.append("Trying again...");
logger.info(message.toString());
}
} catch (InterruptedException ex) {
// re-assert interrupt state that occurred while we
// were acquiring the lock
Thread.currentThread().interrupt();
}
return locked;
}
/**
* A thread that previously propagated the lock set from another thread by using {@link #propagateLockSetFrom(Thread)}
* ends the propagation with this call. After this call, <code>thread</code> is free to release any locks it held
* at the time {@link #propagateLockSetFrom(Thread)} was called by the current thread with <code>thread</code> as
* the argument.
*/
public static void unpropagateLockSetFrom(Thread thread) {
Map<Lock, Integer> otherMap = lockCounts.get(thread);
if (otherMap != null) {
Map<Lock, Integer> currentMap = getCurrentThreadsLockCounts();
for (Map.Entry<Lock, Integer> otherEntry : otherMap.entrySet()) {
assert currentMap.containsKey(otherEntry.getKey());
currentMap.put(otherEntry.getKey(), currentMap.get(otherEntry.getKey()) - otherEntry.getValue());
}
}
private static void appendThreadData(StringBuilder message, Thread writer) {
message.append(writer);
message.append('\n');
message.append(getStackTrace(writer));
message.append('\n');
}
}