/** * 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()]); }
/** * 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 this thread either owns, or is waiting for, any locks or rules. */ public boolean isLockOwner() { //all job threads have to be treated as lock owners because UI thread //may try to join a job Thread current = Thread.currentThread(); if (current instanceof Worker) return true; DeadlockDetector tempLocks = locks; if (tempLocks == null) return false; synchronized (tempLocks) { return tempLocks.contains(Thread.currentThread()); } }
/** * 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); }
/** * 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); }
/** * 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; }
/** * 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); }
/** * 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; }
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;
/** * 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); Thread candidate = resolutionCandidate(threads); ISchedulingRule[] locksToSuspend = realLocksForThread(candidate); Deadlock deadlock = new Deadlock(threads, locksToSuspend, candidate); //find a thread whose locks can be suspended to resolve the deadlock if (JobManager.DEBUG_LOCKS) 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); }
/** * 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); }
/** * 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; }
/** * 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); }
/** * 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); }
/** * 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; }
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;
/** * 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; }