1 11 package org.eclipse.core.internal.jobs; 12 13 import java.io.PrintWriter ; 14 import java.io.StringWriter ; 15 import java.util.ArrayList ; 16 import org.eclipse.core.internal.runtime.RuntimeLog; 17 import org.eclipse.core.runtime.*; 18 import org.eclipse.core.runtime.jobs.ILock; 19 import org.eclipse.core.runtime.jobs.ISchedulingRule; 20 21 68 class DeadlockDetector { 69 private static int NO_STATE = 0; 70 private static int WAITING_FOR_LOCK = -1; 72 private static final int[][] EMPTY_MATRIX = new int[0][0]; 74 private int[][] graph = EMPTY_MATRIX; 76 private final ArrayList locks = new ArrayList (); 78 private final ArrayList lockThreads = new ArrayList (); 80 private boolean resize = false; 82 83 88 private boolean addCycleThreads(ArrayList deadlockedThreads, Thread next) { 89 Thread [] blocking = blockingThreads(next); 91 if (blocking.length == 0) 93 return false; 94 boolean inCycle = false; 95 for (int i = 0; i < blocking.length; i++) { 96 if (deadlockedThreads.contains(blocking[i])) { 98 inCycle = true; 99 } else { 100 deadlockedThreads.add(blocking[i]); 102 if (addCycleThreads(deadlockedThreads, blocking[i])) 104 inCycle = true; 105 else 106 deadlockedThreads.remove(blocking[i]); 107 } 108 } 109 return inCycle; 110 } 111 112 115 private Thread [] blockingThreads(Thread current) { 116 ISchedulingRule lock = (ISchedulingRule) getWaitingLock(current); 118 return getThreadsOwningLock(lock); 119 } 120 121 125 private boolean checkWaitCycles(int[] waitingThreads, int lockIndex) { 126 130 for (int i = 0; i < graph.length; i++) { 131 if (graph[i][lockIndex] > NO_STATE) { 132 if (waitingThreads[i] > NO_STATE) { 133 return true; 134 } 135 waitingThreads[i]++; 137 for (int j = 0; j < graph[i].length; j++) { 138 if (graph[i][j] == WAITING_FOR_LOCK) { 139 if (checkWaitCycles(waitingThreads, j)) 140 return true; 141 } 142 } 143 waitingThreads[i]--; 145 } 146 } 147 return false; 148 } 149 150 154 boolean contains(Thread t) { 155 return lockThreads.contains(t); 156 } 157 158 163 private void fillPresentEntries(ISchedulingRule newLock, int lockIndex) { 164 for (int j = 0; j < locks.size(); j++) { 166 if ((j != lockIndex) && (newLock.isConflicting((ISchedulingRule) locks.get(j)))) { 167 for (int i = 0; i < graph.length; i++) { 168 if ((graph[i][j] > NO_STATE) && (graph[i][lockIndex] == NO_STATE)) 169 graph[i][lockIndex] = graph[i][j]; 170 } 171 } 172 } 173 for (int j = 0; j < locks.size(); j++) { 175 if ((j != lockIndex) && (newLock.isConflicting((ISchedulingRule) locks.get(j)))) { 176 for (int i = 0; i < graph.length; i++) { 177 if ((graph[i][lockIndex] > NO_STATE) && (graph[i][j] == NO_STATE)) 178 graph[i][j] = graph[i][lockIndex]; 179 } 180 } 181 } 182 } 183 184 187 private Object [] getOwnedLocks(Thread current) { 188 ArrayList ownedLocks = new ArrayList (1); 189 int index = indexOf(current, false); 190 191 for (int j = 0; j < graph[index].length; j++) { 192 if (graph[index][j] > NO_STATE) 193 ownedLocks.add(locks.get(j)); 194 } 195 if (ownedLocks.size() == 0) 196 Assert.isLegal(false, "A thread with no locks is part of a deadlock."); return ownedLocks.toArray(); 198 } 199 200 203 private Thread [] getThreadsInDeadlock(Thread cause) { 204 ArrayList deadlockedThreads = new ArrayList (2); 205 209 if (ownsLocks(cause)) 210 deadlockedThreads.add(cause); 211 addCycleThreads(deadlockedThreads, cause); 212 return (Thread []) deadlockedThreads.toArray(new Thread [deadlockedThreads.size()]); 213 } 214 215 218 private Thread [] getThreadsOwningLock(ISchedulingRule rule) { 219 if (rule == null) 220 return new Thread [0]; 221 int lockIndex = indexOf(rule, false); 222 ArrayList blocking = new ArrayList (1); 223 for (int i = 0; i < graph.length; i++) { 224 if (graph[i][lockIndex] > NO_STATE) 225 blocking.add(lockThreads.get(i)); 226 } 227 if ((blocking.size() == 0) && (JobManager.DEBUG_LOCKS)) 228 System.out.println("Lock " + rule + " is involved in deadlock but is not owned by any thread."); if ((blocking.size() > 1) && (rule instanceof ILock) && (JobManager.DEBUG_LOCKS)) 230 System.out.println("Lock " + rule + " is owned by more than 1 thread, but it is not a rule."); return (Thread []) blocking.toArray(new Thread [blocking.size()]); 232 } 233 234 237 private Object getWaitingLock(Thread current) { 238 int index = indexOf(current, false); 239 for (int j = 0; j < graph[index].length; j++) { 241 if (graph[index][j] == WAITING_FOR_LOCK) 242 return locks.get(j); 243 } 244 return null; 246 } 247 248 252 private int indexOf(ISchedulingRule lock, boolean add) { 253 int index = locks.indexOf(lock); 254 if ((index < 0) && add) { 255 locks.add(lock); 256 resize = true; 257 index = locks.size() - 1; 258 } 259 return index; 260 } 261 262 266 private int indexOf(Thread owner, boolean add) { 267 int index = lockThreads.indexOf(owner); 268 if ((index < 0) && add) { 269 lockThreads.add(owner); 270 resize = true; 271 index = lockThreads.size() - 1; 272 } 273 return index; 274 } 275 276 279 boolean isEmpty() { 280 return (locks.size() == 0) && (lockThreads.size() == 0) && (graph.length == 0); 281 } 282 283 286 void lockAcquired(Thread owner, ISchedulingRule lock) { 287 int lockIndex = indexOf(lock, true); 288 int threadIndex = indexOf(owner, true); 289 if (resize) 290 resizeGraph(); 291 if (graph[threadIndex][lockIndex] == WAITING_FOR_LOCK) 292 graph[threadIndex][lockIndex] = NO_STATE; 293 298 ArrayList conflicting = new ArrayList (1); 299 int NUM_PASSES = 2; 301 conflicting.add(lock); 302 graph[threadIndex][lockIndex]++; 303 for (int i = 0; i < NUM_PASSES; i++) { 304 for (int k = 0; k < conflicting.size(); k++) { 305 ISchedulingRule current = (ISchedulingRule) conflicting.get(k); 306 for (int j = 0; j < locks.size(); j++) { 307 ISchedulingRule possible = (ISchedulingRule) locks.get(j); 308 if (current.isConflicting(possible) && !conflicting.contains(possible)) { 309 conflicting.add(possible); 310 graph[threadIndex][j]++; 311 } 312 } 313 } 314 } 315 } 316 317 320 void lockReleased(Thread owner, ISchedulingRule lock) { 321 int lockIndex = indexOf(lock, false); 322 int threadIndex = indexOf(owner, false); 323 if (threadIndex < 0) { 325 if (JobManager.DEBUG_LOCKS) 326 System.out.println("[lockReleased] Lock " + lock + " was already released by thread " + owner.getName()); return; 328 } 329 if (lockIndex < 0) { 330 if (JobManager.DEBUG_LOCKS) 331 System.out.println("[lockReleased] Thread " + owner.getName() + " already released lock " + lock); return; 333 } 334 if ((lock instanceof ILock) && (graph[threadIndex][lockIndex] == WAITING_FOR_LOCK)) { 336 graph[threadIndex][lockIndex] = NO_STATE; 337 return; 338 } 339 for (int j = 0; j < graph[threadIndex].length; j++) { 342 if ((lock.isConflicting((ISchedulingRule) locks.get(j))) || (!(lock instanceof ILock) && !(locks.get(j) instanceof ILock) && (graph[threadIndex][j] > NO_STATE))) { 343 if (graph[threadIndex][j] == NO_STATE) { 344 if (JobManager.DEBUG_LOCKS) 345 System.out.println("[lockReleased] More releases than acquires for thread " + owner.getName() + " and lock " + lock); } else { 347 graph[threadIndex][j]--; 348 } 349 } 350 } 351 if (graph[threadIndex][lockIndex] == NO_STATE) 353 reduceGraph(threadIndex, lock); 354 } 355 356 360 void lockReleasedCompletely(Thread owner, ISchedulingRule rule) { 361 int ruleIndex = indexOf(rule, false); 362 int threadIndex = indexOf(owner, false); 363 if (threadIndex < 0) { 365 if (JobManager.DEBUG_LOCKS) 366 System.out.println("[lockReleasedCompletely] Lock " + rule + " was already released by thread " + owner.getName()); return; 368 } 369 if (ruleIndex < 0) { 370 if (JobManager.DEBUG_LOCKS) 371 System.out.println("[lockReleasedCompletely] Thread " + owner.getName() + " already released lock " + rule); return; 373 } 374 379 for (int j = 0; j < graph[threadIndex].length; j++) { 380 if (!(locks.get(j) instanceof ILock) && (graph[threadIndex][j] > NO_STATE)) 381 graph[threadIndex][j] = NO_STATE; 382 } 383 reduceGraph(threadIndex, rule); 384 } 385 386 390 Deadlock lockWaitStart(Thread client, ISchedulingRule lock) { 391 setToWait(client, lock, false); 392 int lockIndex = indexOf(lock, false); 393 int[] temp = new int[lockThreads.size()]; 394 if (!checkWaitCycles(temp, lockIndex)) 396 return null; 397 Thread [] threads = getThreadsInDeadlock(client); 399 Thread candidate = resolutionCandidate(threads); 400 ISchedulingRule[] locksToSuspend = realLocksForThread(candidate); 401 Deadlock deadlock = new Deadlock(threads, locksToSuspend, candidate); 402 if (JobManager.DEBUG_LOCKS) 404 reportDeadlock(deadlock); 405 if (JobManager.DEBUG_DEADLOCK) 406 throw new IllegalStateException ("Deadlock detected. Caused by thread " + client.getName() + '.'); for (int i = 0; i < locksToSuspend.length; i++) 411 setToWait(deadlock.getCandidate(), locksToSuspend[i], true); 412 return deadlock; 413 } 414 415 419 void lockWaitStop(Thread owner, ISchedulingRule lock) { 420 int lockIndex = indexOf(lock, false); 421 int threadIndex = indexOf(owner, false); 422 if (threadIndex < 0) { 424 if (JobManager.DEBUG_LOCKS) 425 System.out.println("Thread " + owner.getName() + " was already removed."); return; 427 } 428 if (lockIndex < 0) { 429 if (JobManager.DEBUG_LOCKS) 430 System.out.println("Lock " + lock + " was already removed."); return; 432 } 433 if (graph[threadIndex][lockIndex] != WAITING_FOR_LOCK) 434 Assert.isTrue(false, "Thread " + owner.getName() + " was not waiting for lock " + lock.toString() + " so it could not time out."); graph[threadIndex][lockIndex] = NO_STATE; 436 reduceGraph(threadIndex, lock); 437 } 438 439 442 private boolean ownsLocks(Thread cause) { 443 int threadIndex = indexOf(cause, false); 444 for (int j = 0; j < graph[threadIndex].length; j++) { 445 if (graph[threadIndex][j] > NO_STATE) 446 return true; 447 } 448 return false; 449 } 450 451 455 private boolean ownsRealLocks(Thread owner) { 456 int threadIndex = indexOf(owner, false); 457 for (int j = 0; j < graph[threadIndex].length; j++) { 458 if (graph[threadIndex][j] > NO_STATE) { 459 Object lock = locks.get(j); 460 if (lock instanceof ILock) 461 return true; 462 } 463 } 464 return false; 465 } 466 467 471 private boolean ownsRuleLocks(Thread owner) { 472 int threadIndex = indexOf(owner, false); 473 for (int j = 0; j < graph[threadIndex].length; j++) { 474 if (graph[threadIndex][j] > NO_STATE) { 475 Object lock = locks.get(j); 476 if (!(lock instanceof ILock)) 477 return true; 478 } 479 } 480 return false; 481 } 482 483 487 private ISchedulingRule[] realLocksForThread(Thread owner) { 488 int threadIndex = indexOf(owner, false); 489 ArrayList ownedLocks = new ArrayList (1); 490 for (int j = 0; j < graph[threadIndex].length; j++) { 491 if ((graph[threadIndex][j] > NO_STATE) && (locks.get(j) instanceof ILock)) 492 ownedLocks.add(locks.get(j)); 493 } 494 if (ownedLocks.size() == 0) 495 Assert.isLegal(false, "A thread with no real locks was chosen to resolve deadlock."); return (ISchedulingRule[]) ownedLocks.toArray(new ISchedulingRule[ownedLocks.size()]); 497 } 498 499 503 private void reduceGraph(int row, ISchedulingRule lock) { 504 int numLocks = locks.size(); 505 boolean[] emptyColumns = new boolean[numLocks]; 506 507 511 for (int j = 0; j < numLocks; j++) { 512 if ((lock.isConflicting((ISchedulingRule) locks.get(j))) || !(locks.get(j) instanceof ILock)) 513 emptyColumns[j] = true; 514 } 515 516 boolean rowEmpty = true; 517 int numEmpty = 0; 518 for (int j = 0; j < graph[row].length; j++) { 520 if (graph[row][j] != NO_STATE) { 521 rowEmpty = false; 522 break; 523 } 524 } 525 531 for (int j = emptyColumns.length - 1; j >= 0; j--) { 532 for (int i = 0; i < graph.length; i++) { 533 if (emptyColumns[j] && (graph[i][j] != NO_STATE)) { 534 emptyColumns[j] = false; 535 break; 536 } 537 } 538 if (emptyColumns[j]) { 539 locks.remove(j); 540 numEmpty++; 541 } 542 } 543 if ((numEmpty == 0) && (!rowEmpty)) 545 return; 546 547 if (rowEmpty) 548 lockThreads.remove(row); 549 550 final int numThreads = lockThreads.size(); 552 numLocks = locks.size(); 553 if (numThreads == 0 && numLocks == 0) { 555 graph = EMPTY_MATRIX; 556 return; 557 } 558 int[][] tempGraph = new int[numThreads][numLocks]; 559 560 int numRowsSkipped = 0; 562 for (int i = 0; i < graph.length - numRowsSkipped; i++) { 563 if ((i == row) && rowEmpty) { 564 numRowsSkipped++; 565 if (i >= graph.length - numRowsSkipped) 567 break; 568 } 569 int numColsSkipped = 0; 572 for (int j = 0; j < graph[i].length - numColsSkipped; j++) { 573 while (emptyColumns[j + numColsSkipped]) { 574 numColsSkipped++; 575 if (j >= graph[i].length - numColsSkipped) 577 break; 578 } 579 if (j >= graph[i].length - numColsSkipped) 581 break; 582 tempGraph[i][j] = graph[i + numRowsSkipped][j + numColsSkipped]; 583 } 584 } 585 graph = tempGraph; 586 Assert.isTrue(numThreads == graph.length, "Rows and threads don't match."); Assert.isTrue(numLocks == ((graph.length > 0) ? graph[0].length : 0), "Columns and locks don't match."); } 589 590 593 private void reportDeadlock(Deadlock deadlock) { 594 String msg = "Deadlock detected. All locks owned by thread " + deadlock.getCandidate().getName() + " will be suspended."; MultiStatus main = new MultiStatus(JobManager.PI_JOBS, JobManager.PLUGIN_ERROR, msg, new IllegalStateException ()); 596 Thread [] threads = deadlock.getThreads(); 597 for (int i = 0; i < threads.length; i++) { 598 Object [] ownedLocks = getOwnedLocks(threads[i]); 599 Object waitLock = getWaitingLock(threads[i]); 600 StringBuffer buf = new StringBuffer ("Thread "); buf.append(threads[i].getName()); 602 buf.append(" has locks: "); for (int j = 0; j < ownedLocks.length; j++) { 604 buf.append(ownedLocks[j]); 605 buf.append((j < ownedLocks.length - 1) ? ", " : " "); } 607 buf.append("and is waiting for lock "); buf.append(waitLock); 609 Status child = new Status(IStatus.ERROR, JobManager.PI_JOBS, JobManager.PLUGIN_ERROR, buf.toString(), null); 610 main.add(child); 611 } 612 RuntimeLog.log(main); 613 } 614 615 619 private void resizeGraph() { 620 final int newRows = lockThreads.size(); 624 final int newCols = locks.size(); 625 if (newRows == 0 && newCols == 0) { 627 graph = EMPTY_MATRIX; 628 return; 629 } 630 int[][] tempGraph = new int[newRows][newCols]; 631 for (int i = 0; i < graph.length; i++) 632 System.arraycopy(graph[i], 0, tempGraph[i], 0, graph[i].length); 633 graph = tempGraph; 634 resize = false; 635 } 636 637 641 private Thread resolutionCandidate(Thread [] candidates) { 642 for (int i = 0; i < candidates.length; i++) { 644 if (!ownsRuleLocks(candidates[i])) 645 return candidates[i]; 646 } 647 for (int i = 0; i < candidates.length; i++) { 649 if (ownsRealLocks(candidates[i])) 650 return candidates[i]; 651 } 652 return candidates[0]; 654 } 655 656 659 private void setToWait(Thread owner, ISchedulingRule lock, boolean suspend) { 660 boolean needTransfer = false; 661 666 if (!suspend && !(lock instanceof ILock)) 667 needTransfer = true; 668 int lockIndex = indexOf(lock, !suspend); 669 int threadIndex = indexOf(owner, !suspend); 670 if (resize) 671 resizeGraph(); 672 673 graph[threadIndex][lockIndex] = WAITING_FOR_LOCK; 674 if (needTransfer) 675 fillPresentEntries(lock, lockIndex); 676 } 677 678 682 public String toDebugString() { 683 StringWriter sWriter = new StringWriter (); 684 PrintWriter out = new PrintWriter (sWriter, true); 685 out.println(" :: "); for (int j = 0; j < locks.size(); j++) { 687 out.print(" " + locks.get(j) + ','); } 689 out.println(); 690 for (int i = 0; i < graph.length; i++) { 691 out.print(" " + ((Thread ) lockThreads.get(i)).getName() + " : "); for (int j = 0; j < graph[i].length; j++) { 693 out.print(" " + graph[i][j] + ','); } 695 out.println(); 696 } 697 out.println("-------"); return sWriter.toString(); 699 } 700 } 701 | Popular Tags |