blob: 3572e0321cd6b7ad2bfe26ad8092ce1c85b1837b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2003, 2012 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.core.internal.jobs;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import org.eclipse.core.internal.runtime.RuntimeLog;
import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.jobs.ILock;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
/**
* Stores all the relationships between locks (rules are also considered locks),
* and the threads that own them. All the relationships are stored in a 2D integer array.
* The rows in the array are threads, while the columns are locks.
* Two corresponding arrayLists store the actual threads and locks.
* The index of a thread in the first arrayList is the index of the row in the graph.
* The index of a lock in the second arrayList is the index of the column in the graph.
* An entry greater than 0 in the graph is the number of times a thread in the entry's row
* acquired the lock in the entry's column.
* An entry of -1 means that the thread is waiting to acquire the lock.
* An entry of 0 means that the thread and the lock have no relationship.
*
* The difference between rules and locks is that locks can be suspended, while
* rules are implicit locks and as such cannot be suspended.
* To resolve deadlock, the graph will first try to find a thread that only owns
* locks. Failing that, it will find a thread in the deadlock that owns at least
* one lock and suspend it.
*
* Deadlock can only occur among locks, or among locks in combination with rules.
* Deadlock among rules only is impossible. Therefore, in any deadlock one can always
* find a thread that owns at least one lock that can be suspended.
*
* The implementation of the graph assumes that a thread can only own 1 rule at
* any one time. It can acquire that rule several times, but a thread cannot
* acquire 2 non-conflicting rules at the same time.
*
* The implementation of the graph will sometimes also find and resolve bogus deadlocks.
* graph: assuming this rule hierarchy:
* R2 R3 L1 R1
* J1 1 0 0 / \
* J2 0 1 -1 R2 R3
* J3 -1 0 1
*
* If in the above situation job4 decides to acquire rule1, then the graph will transform
* to the following:
* R2 R3 R1 L1
* J1 1 0 1 0
* J2 1 1 1 -1
* J3 -1 0 0 1
* J4 0 0 -1 0
*
* and the graph will assume that job2 and job3 are deadlocked and suspend lock1 of job3.
* The reason the deadlock is bogus is that the deadlock is unlikely to actually happen (the threads
* are currently not deadlocked, but might deadlock later on when it is too late to detect it)
* Therefore, in order to make sure that no deadlock is possible,
* the deadlock will still be resolved at this point.
*/
class DeadlockDetector {
private static int NO_STATE = 0;
//state variables in the graph
private static int WAITING_FOR_LOCK = -1;
//empty matrix
private static final int[][] EMPTY_MATRIX = new int[0][0];
//matrix of relationships between threads and locks
private int[][] graph = EMPTY_MATRIX;
//index is column in adjacency matrix for the lock
private final ArrayList<ISchedulingRule> locks = new ArrayList<>();
//index is row in adjacency matrix for the thread
private final ArrayList<Thread> lockThreads = new ArrayList<>();
//whether the graph needs to be resized
private boolean resize = false;
/**
* Recursively check if any of the threads that prevent the current thread from running
* are actually deadlocked with the current thread.
* Add the threads that form deadlock to the deadlockedThreads list.
*/
private boolean addCycleThreads(ArrayList<Thread> deadlockedThreads, Thread next) {
//get the thread that block the given thread from running
Thread[] blocking = blockingThreads(next);
//if the thread is not blocked by other threads, then it is not part of a deadlock
if (blocking.length == 0)
return false;
boolean inCycle = false;
for (int i = 0; i < blocking.length; i++) {
//if we have already visited the given thread, then we found a cycle
if (deadlockedThreads.contains(blocking[i])) {
inCycle = true;
} else {
//otherwise, add the thread to our list and recurse deeper
deadlockedThreads.add(blocking[i]);
//if the thread is not part of a cycle, remove it from the list
if (addCycleThreads(deadlockedThreads, blocking[i]))
inCycle = true;
else
deadlockedThreads.remove(blocking[i]);
}
}
return inCycle;
}
/**
* Get the thread(s) that own the lock this thread is waiting for.
*/
private Thread[] blockingThreads(Thread current) {
//find the lock this thread is waiting for
ISchedulingRule lock = (ISchedulingRule) getWaitingLock(current);
return getThreadsOwningLock(lock);
}
/**
* Check that the addition of a waiting thread did not produce deadlock.
* If deadlock is detected return true, else return false.
*/
private boolean checkWaitCycles(int[] waitingThreads, int lockIndex) {
/**
* find the lock that this thread is waiting for
* recursively check if this is a cycle (i.e. a thread waiting on itself)
*/
for (int i = 0; i < graph.length; i++) {
if (graph[i][lockIndex] > NO_STATE) {
if (waitingThreads[i] > NO_STATE) {
return true;
}
//keep track that we already visited this thread
waitingThreads[i]++;
for (int j = 0; j < graph[i].length; j++) {
if (graph[i][j] == WAITING_FOR_LOCK) {
if (checkWaitCycles(waitingThreads, j))
return true;
}
}
//this thread is not involved in a cycle yet, so remove the visited flag
waitingThreads[i]--;
}
}
return false;
}
/**
* Returns true IFF the matrix contains a row for the given thread.
* (meaning the given thread either owns locks or is waiting for locks)
*/
boolean contains(Thread t) {
return lockThreads.contains(t);
}
/**
* A new rule was just added to the graph.
* Find a rule it conflicts with and update the new rule with the number of times
* it was acquired implicitly when threads acquired conflicting rule.
*/
private void fillPresentEntries(ISchedulingRule newLock, int lockIndex) {
//fill in the entries for the new rule from rules it conflicts with
for (int j = 0; j < locks.size(); j++) {
if ((j != lockIndex) && (newLock.isConflicting(locks.get(j)))) {
for (int i = 0; i < graph.length; i++) {
if ((graph[i][j] > NO_STATE) && (graph[i][lockIndex] == NO_STATE))
graph[i][lockIndex] = graph[i][j];
}
}
}
//now back fill the entries for rules the current rule conflicts with
for (int j = 0; j < locks.size(); j++) {
if ((j != lockIndex) && (newLock.isConflicting(locks.get(j)))) {
for (int i = 0; i < graph.length; i++) {
if ((graph[i][lockIndex] > NO_STATE) && (graph[i][j] == NO_STATE))
graph[i][j] = graph[i][lockIndex];
}
}
}
}
/**
* Returns all the locks owned by the given thread
*/
private Object[] getOwnedLocks(Thread current) {
ArrayList<ISchedulingRule> ownedLocks = new ArrayList<>(1);
int index = indexOf(current, false);
for (int j = 0; j < graph[index].length; j++) {
if (graph[index][j] > NO_STATE)
ownedLocks.add(locks.get(j));
}
if (ownedLocks.size() == 0)
Assert.isLegal(false, "A thread with no locks is part of a deadlock."); //$NON-NLS-1$
return ownedLocks.toArray();
}
/**
* Returns an array of threads that form the deadlock (usually 2).
*/
private Thread[] getThreadsInDeadlock(Thread cause) {
ArrayList<Thread> deadlockedThreads = new ArrayList<>(2);
/**
* if the thread that caused deadlock doesn't own any locks, then it is not part
* of the deadlock (it just caused it because of a rule it tried to acquire)
*/
if (ownsLocks(cause))
deadlockedThreads.add(cause);
addCycleThreads(deadlockedThreads, cause);
return deadlockedThreads.toArray(new Thread[deadlockedThreads.size()]);
}
/**
* Returns the thread(s) that own the given lock.
*/
private Thread[] getThreadsOwningLock(ISchedulingRule rule) {
if (rule == null)
return new Thread[0];
int lockIndex = indexOf(rule, false);
ArrayList<Thread> blocking = new ArrayList<>(1);
for (int i = 0; i < graph.length; i++) {
if (graph[i][lockIndex] > NO_STATE)
blocking.add(lockThreads.get(i));
}
if ((blocking.size() == 0) && (JobManager.DEBUG_LOCKS))
System.out.println("Lock " + rule + " is involved in deadlock but is not owned by any thread."); //$NON-NLS-1$ //$NON-NLS-2$
if ((blocking.size() > 1) && (rule instanceof ILock) && (JobManager.DEBUG_LOCKS))
System.out.println("Lock " + rule + " is owned by more than 1 thread, but it is not a rule."); //$NON-NLS-1$ //$NON-NLS-2$
return blocking.toArray(new Thread[blocking.size()]);
}
/**
* Returns the lock the given thread is waiting for.
*/
private Object getWaitingLock(Thread current) {
int index = indexOf(current, false);
//find the lock that this thread is waiting for
for (int j = 0; j < graph[index].length; j++) {
if (graph[index][j] == WAITING_FOR_LOCK)
return locks.get(j);
}
//it can happen that a thread is not waiting for any lock (it is not really part of the deadlock)
return null;
}
/**
* Returns the index of the given lock in the lock array. If the lock is
* not present in the array, it is added to the end.
*/
private int indexOf(ISchedulingRule lock, boolean add) {
int index = locks.indexOf(lock);
if ((index < 0) && add) {
locks.add(lock);
resize = true;
index = locks.size() - 1;
}
return index;
}
/**
* Returns the index of the given thread in the thread array. If the thread
* is not present in the array, it is added to the end.
*/
private int indexOf(Thread owner, boolean add) {
int index = lockThreads.indexOf(owner);
if ((index < 0) && add) {
lockThreads.add(owner);
resize = true;
index = lockThreads.size() - 1;
}
return index;
}
/**
* Returns true IFF the adjacency matrix is empty.
*/
boolean isEmpty() {
return (locks.size() == 0) && (lockThreads.size() == 0) && (graph.length == 0);
}
/**
* The given lock was acquired by the given thread.
*/
void lockAcquired(Thread owner, ISchedulingRule lock) {
int lockIndex = indexOf(lock, true);
int threadIndex = indexOf(owner, true);
if (resize)
resizeGraph();
if (graph[threadIndex][lockIndex] == WAITING_FOR_LOCK)
graph[threadIndex][lockIndex] = NO_STATE;
/**
* acquire all locks that conflict with the given lock
* or conflict with a lock the given lock will acquire implicitly
* (locks are acquired implicitly when a conflicting lock is acquired)
*/
ArrayList<ISchedulingRule> conflicting = new ArrayList<>(1);
//only need two passes through all the locks to pick up all conflicting rules
int NUM_PASSES = 2;
conflicting.add(lock);
graph[threadIndex][lockIndex]++;
for (int i = 0; i < NUM_PASSES; i++) {
for (int k = 0; k < conflicting.size(); k++) {
ISchedulingRule current = conflicting.get(k);
for (int j = 0; j < locks.size(); j++) {
ISchedulingRule possible = locks.get(j);
if (current.isConflicting(possible) && !conflicting.contains(possible)) {
conflicting.add(possible);
graph[threadIndex][j]++;
}
}
}
}
}
/**
* The given lock was released by the given thread. Update the graph.
*/
void lockReleased(Thread owner, ISchedulingRule lock) {
int lockIndex = indexOf(lock, false);
int threadIndex = indexOf(owner, false);
//make sure the lock and thread exist in the graph
if (threadIndex < 0) {
if (JobManager.DEBUG_LOCKS)
System.out.println("[lockReleased] Lock " + lock + " was already released by thread " + owner.getName()); //$NON-NLS-1$ //$NON-NLS-2$
return;
}
if (lockIndex < 0) {
if (JobManager.DEBUG_LOCKS)
System.out.println("[lockReleased] Thread " + owner.getName() + " already released lock " + lock); //$NON-NLS-1$ //$NON-NLS-2$
return;
}
//if this lock was suspended, set it to NO_STATE
if ((lock instanceof ILock) && (graph[threadIndex][lockIndex] == WAITING_FOR_LOCK)) {
graph[threadIndex][lockIndex] = NO_STATE;
return;
}
//release all locks that conflict with the given lock
//or release all rules that are owned by the given thread, if we are releasing a rule
for (int j = 0; j < graph[threadIndex].length; j++) {
if ((lock.isConflicting(locks.get(j))) || (!(lock instanceof ILock) && !(locks.get(j) instanceof ILock) && (graph[threadIndex][j] > NO_STATE))) {
if (graph[threadIndex][j] == NO_STATE) {
if (JobManager.DEBUG_LOCKS)
System.out.println("[lockReleased] More releases than acquires for thread " + owner.getName() + " and lock " + lock); //$NON-NLS-1$ //$NON-NLS-2$
} else {
graph[threadIndex][j]--;
}
}
}
//if this thread just released the given lock, try to simplify the graph
if (graph[threadIndex][lockIndex] == NO_STATE)
reduceGraph(threadIndex, lock);
}
/**
* The given scheduling rule is no longer used because the job that invoked it is done.
* Release this rule regardless of how many times it was acquired.
*/
void lockReleasedCompletely(Thread owner, ISchedulingRule rule) {
int ruleIndex = indexOf(rule, false);
int threadIndex = indexOf(owner, false);
//need to make sure that the given thread and rule were not already removed from the graph
if (threadIndex < 0) {
if (JobManager.DEBUG_LOCKS)
System.out.println("[lockReleasedCompletely] Lock " + rule + " was already released by thread " + owner.getName()); //$NON-NLS-1$ //$NON-NLS-2$
return;
}
if (ruleIndex < 0) {
if (JobManager.DEBUG_LOCKS)
System.out.println("[lockReleasedCompletely] Thread " + owner.getName() + " already released lock " + rule); //$NON-NLS-1$ //$NON-NLS-2$
return;
}
/**
* set all rules that are owned by the given thread to NO_STATE
* (not just rules that conflict with the rule we are releasing)
* if we are releasing a lock, then only update the one entry for the lock
*/
for (int j = 0; j < graph[threadIndex].length; j++) {
if (!(locks.get(j) instanceof ILock) && (graph[threadIndex][j] > NO_STATE))
graph[threadIndex][j] = NO_STATE;
}
reduceGraph(threadIndex, rule);
}
/**
* The given thread could not get the given lock and is waiting for it.
* Update the graph.
*/
Deadlock lockWaitStart(Thread client, ISchedulingRule lock) {
setToWait(client, lock, false);
int lockIndex = indexOf(lock, false);
int[] temp = new int[lockThreads.size()];
//check if the addition of the waiting thread caused deadlock
if (!checkWaitCycles(temp, lockIndex))
return null;
//there is a deadlock in the graph
Thread[] threads = getThreadsInDeadlock(client);
//find a thread whose locks can be suspended to resolve the deadlock
Thread candidate = resolutionCandidate(threads);
ISchedulingRule[] locksToSuspend = realLocksForThread(candidate);
Deadlock deadlock = new Deadlock(threads, locksToSuspend, candidate);
reportDeadlock(deadlock);
if (JobManager.DEBUG_DEADLOCK)
throw new IllegalStateException("Deadlock detected. Caused by thread " + client.getName() + '.'); //$NON-NLS-1$
// Update the graph to indicate that the locks will now be suspended.
// To indicate that the lock will be suspended, we set the thread to wait for the lock.
// When the lock is forced to be released, the entry will be cleared.
for (int i = 0; i < locksToSuspend.length; i++)
setToWait(deadlock.getCandidate(), locksToSuspend[i], true);
return deadlock;
}
/**
* The given thread has stopped waiting for the given lock.
* Update the graph.
* If the lock has already been granted, then it isn't removed.
*/
void lockWaitStop(Thread owner, ISchedulingRule lock) {
int lockIndex = indexOf(lock, false);
int threadIndex = indexOf(owner, false);
//make sure the thread and lock exist in the graph
if (threadIndex < 0) {
if (JobManager.DEBUG_LOCKS)
System.out.println("Thread " + owner.getName() + " was already removed."); //$NON-NLS-1$ //$NON-NLS-2$
return;
}
if (lockIndex < 0) {
if (JobManager.DEBUG_LOCKS)
System.out.println("Lock " + lock + " was already removed."); //$NON-NLS-1$ //$NON-NLS-2$
return;
}
if (graph[threadIndex][lockIndex] != WAITING_FOR_LOCK) {
// Lock has already been granted, nothing to do...
if (JobManager.DEBUG_LOCKS)
System.out.println("Lock " + lock + " already granted to depth: " + graph[threadIndex][lockIndex]); //$NON-NLS-1$ //$NON-NLS-2$
return;
}
graph[threadIndex][lockIndex] = NO_STATE;
reduceGraph(threadIndex, lock);
}
/**
* Returns true IFF the given thread owns a single lock
*/
private boolean ownsLocks(Thread cause) {
int threadIndex = indexOf(cause, false);
for (int j = 0; j < graph[threadIndex].length; j++) {
if (graph[threadIndex][j] > NO_STATE)
return true;
}
return false;
}
/**
* Returns true IFF the given thread owns a single real lock.
* A real lock is a lock that can be suspended.
*/
private boolean ownsRealLocks(Thread owner) {
int threadIndex = indexOf(owner, false);
for (int j = 0; j < graph[threadIndex].length; j++) {
if (graph[threadIndex][j] > NO_STATE) {
Object lock = locks.get(j);
if (lock instanceof ILock)
return true;
}
}
return false;
}
/**
* Return true IFF this thread owns rule locks (i.e. implicit locks which
* cannot be suspended)
*/
private boolean ownsRuleLocks(Thread owner) {
int threadIndex = indexOf(owner, false);
for (int j = 0; j < graph[threadIndex].length; j++) {
if (graph[threadIndex][j] > NO_STATE) {
Object lock = locks.get(j);
if (!(lock instanceof ILock))
return true;
}
}
return false;
}
/**
* Returns an array of real locks that are owned by the given thread.
* Real locks are locks that implement the ILock interface and can be suspended.
*/
private ISchedulingRule[] realLocksForThread(Thread owner) {
int threadIndex = indexOf(owner, false);
ArrayList<ISchedulingRule> ownedLocks = new ArrayList<>(1);
for (int j = 0; j < graph[threadIndex].length; j++) {
if ((graph[threadIndex][j] > NO_STATE) && (locks.get(j) instanceof ILock))
ownedLocks.add(locks.get(j));
}
if (ownedLocks.size() == 0)
Assert.isLegal(false, "A thread with no real locks was chosen to resolve deadlock."); //$NON-NLS-1$
return ownedLocks.toArray(new ISchedulingRule[ownedLocks.size()]);
}
/**
* The matrix has been simplified. Check if any unnecessary rows or columns
* can be removed.
*/
private void reduceGraph(int row, ISchedulingRule lock) {
int numLocks = locks.size();
boolean[] emptyColumns = new boolean[numLocks];
/**
* find all columns that could possibly be empty
* (consist of locks which conflict with the given lock, or of locks which are rules)
*/
for (int j = 0; j < numLocks; j++) {
if ((lock.isConflicting(locks.get(j))) || !(locks.get(j) instanceof ILock))
emptyColumns[j] = true;
}
boolean rowEmpty = true;
int numEmpty = 0;
//check if the given row is empty
for (int j = 0; j < graph[row].length; j++) {
if (graph[row][j] != NO_STATE) {
rowEmpty = false;
break;
}
}
/**
* Check if the possibly empty columns are actually empty.
* If a column is actually empty, remove the corresponding lock from the list of locks
* Start at the last column so that when locks are removed from the list,
* the index of the remaining locks is unchanged. Store the number of empty columns.
*/
for (int j = emptyColumns.length - 1; j >= 0; j--) {
for (int i = 0; i < graph.length; i++) {
if (emptyColumns[j] && (graph[i][j] != NO_STATE)) {
emptyColumns[j] = false;
break;
}
}
if (emptyColumns[j]) {
locks.remove(j);
numEmpty++;
}
}
//if no columns or rows are empty, return right away
if ((numEmpty == 0) && (!rowEmpty))
return;
if (rowEmpty)
lockThreads.remove(row);
//new graph (the list of locks and the list of threads are already updated)
final int numThreads = lockThreads.size();
numLocks = locks.size();
//optimize empty graph case
if (numThreads == 0 && numLocks == 0) {
graph = EMPTY_MATRIX;
return;
}
int[][] tempGraph = new int[numThreads][numLocks];
//the number of rows we need to skip to get the correct entry from the old graph
int numRowsSkipped = 0;
for (int i = 0; i < graph.length - numRowsSkipped; i++) {
if ((i == row) && rowEmpty) {
numRowsSkipped++;
//check if we need to skip the last row
if (i >= graph.length - numRowsSkipped)
break;
}
//the number of columns we need to skip to get the correct entry from the old graph
//needs to be reset for every new row
int numColsSkipped = 0;
for (int j = 0; j < graph[i].length - numColsSkipped; j++) {
while (emptyColumns[j + numColsSkipped]) {
numColsSkipped++;
//check if we need to skip the last column
if (j >= graph[i].length - numColsSkipped)
break;
}
//need to break out of the outer loop
if (j >= graph[i].length - numColsSkipped)
break;
tempGraph[i][j] = graph[i + numRowsSkipped][j + numColsSkipped];
}
}
graph = tempGraph;
Assert.isTrue(numThreads == graph.length, "Rows and threads don't match."); //$NON-NLS-1$
Assert.isTrue(numLocks == ((graph.length > 0) ? graph[0].length : 0), "Columns and locks don't match."); //$NON-NLS-1$
}
/**
* Adds a 'deadlock detected' message to the log with a stack trace.
*/
private void reportDeadlock(Deadlock deadlock) {
String msg = "Deadlock detected. All locks owned by thread " + deadlock.getCandidate().getName() + " will be suspended."; //$NON-NLS-1$ //$NON-NLS-2$
MultiStatus main = new MultiStatus(JobManager.PI_JOBS, JobManager.PLUGIN_ERROR, msg, new IllegalStateException());
Thread[] threads = deadlock.getThreads();
for (int i = 0; i < threads.length; i++) {
Object[] ownedLocks = getOwnedLocks(threads[i]);
Object waitLock = getWaitingLock(threads[i]);
StringBuffer buf = new StringBuffer("Thread "); //$NON-NLS-1$
buf.append(threads[i].getName());
buf.append(" has locks: "); //$NON-NLS-1$
for (int j = 0; j < ownedLocks.length; j++) {
buf.append(ownedLocks[j]);
buf.append((j < ownedLocks.length - 1) ? ", " : " "); //$NON-NLS-1$ //$NON-NLS-2$
}
buf.append("and is waiting for lock "); //$NON-NLS-1$
buf.append(waitLock);
Status child = new Status(IStatus.ERROR, JobManager.PI_JOBS, JobManager.PLUGIN_ERROR, buf.toString(), null);
main.add(child);
}
RuntimeLog.log(main);
}
/**
* The number of threads/locks in the graph has changed. Update the
* underlying matrix.
*/
private void resizeGraph() {
// a new row and/or a new column was added to the graph.
// since new rows/columns are always added to the end, just transfer
// old entries to the new graph, with the same indices.
final int newRows = lockThreads.size();
final int newCols = locks.size();
//optimize 0x0 and 1x1 matrices
if (newRows == 0 && newCols == 0) {
graph = EMPTY_MATRIX;
return;
}
int[][] tempGraph = new int[newRows][newCols];
for (int i = 0; i < graph.length; i++)
System.arraycopy(graph[i], 0, tempGraph[i], 0, graph[i].length);
graph = tempGraph;
resize = false;
}
/**
* Get the thread whose locks can be suspended. (i.e. all locks it owns are
* actual locks and not rules). Return the first thread in the array by default.
*/
private Thread resolutionCandidate(Thread[] candidates) {
//first look for a candidate that has no scheduling rules
for (int i = 0; i < candidates.length; i++) {
if (!ownsRuleLocks(candidates[i]))
return candidates[i];
}
//next look for any candidate with a real lock (a lock that can be suspended)
for (int i = 0; i < candidates.length; i++) {
if (ownsRealLocks(candidates[i]))
return candidates[i];
}
//unnecessary, return the first entry in the array by default
return candidates[0];
}
/**
* The given thread is waiting for the given lock. Update the graph.
*/
private void setToWait(Thread owner, ISchedulingRule lock, boolean suspend) {
boolean needTransfer = false;
/**
* if we are adding an entry where a thread is waiting on a scheduling rule,
* then we need to transfer all positive entries for a conflicting rule to the
* newly added rule in order to synchronize the graph.
*/
if (!suspend && !(lock instanceof ILock))
needTransfer = true;
int lockIndex = indexOf(lock, !suspend);
int threadIndex = indexOf(owner, !suspend);
if (resize)
resizeGraph();
graph[threadIndex][lockIndex] = WAITING_FOR_LOCK;
if (needTransfer)
fillPresentEntries(lock, lockIndex);
}
/**
* Prints out the current matrix to standard output.
* Only used for debugging.
*/
public String toDebugString() {
StringWriter sWriter = new StringWriter();
PrintWriter out = new PrintWriter(sWriter, true);
out.println(" :: "); //$NON-NLS-1$
for (int j = 0; j < locks.size(); j++) {
out.print(" " + locks.get(j) + ','); //$NON-NLS-1$
}
out.println();
for (int i = 0; i < graph.length; i++) {
out.print(" " + lockThreads.get(i).getName() + " : "); //$NON-NLS-1$ //$NON-NLS-2$
for (int j = 0; j < graph[i].length; j++) {
out.print(" " + graph[i][j] + ','); //$NON-NLS-1$
}
out.println();
}
out.println("-------"); //$NON-NLS-1$
return sWriter.toString();
}
}