1 8 9 package com.sleepycat.je.txn; 10 11 import java.util.HashMap ; 12 import java.util.HashSet ; 13 import java.util.Iterator ; 14 import java.util.Map ; 15 import java.util.Set ; 16 17 import com.sleepycat.je.DatabaseException; 18 import com.sleepycat.je.DeadlockException; 19 import com.sleepycat.je.LockStats; 20 import com.sleepycat.je.RunRecoveryException; 21 import com.sleepycat.je.StatsConfig; 22 import com.sleepycat.je.config.EnvironmentParams; 23 import com.sleepycat.je.dbi.DatabaseImpl; 24 import com.sleepycat.je.dbi.DbConfigManager; 25 import com.sleepycat.je.dbi.EnvConfigObserver; 26 import com.sleepycat.je.dbi.EnvironmentImpl; 27 import com.sleepycat.je.dbi.MemoryBudget; 28 import com.sleepycat.je.dbi.RangeRestartException; 29 import com.sleepycat.je.latch.Latch; 30 import com.sleepycat.je.latch.LatchStats; 31 import com.sleepycat.je.latch.LatchSupport; 32 33 38 public abstract class LockManager implements EnvConfigObserver { 39 40 47 static final long TOTAL_LOCK_OVERHEAD = 48 MemoryBudget.LOCK_OVERHEAD + 49 MemoryBudget.HASHMAP_ENTRY_OVERHEAD + 50 MemoryBudget.LONG_OVERHEAD; 51 52 private static final long REMOVE_TOTAL_LOCK_OVERHEAD = 53 0 - TOTAL_LOCK_OVERHEAD; 54 55 protected int nLockTables = 1; 56 protected Latch[] lockTableLatches; 57 private Map [] lockTables; private EnvironmentImpl envImpl; 59 private MemoryBudget memoryBudget; 60 62 private long nRequests; private long nWaits; 65 private static RangeRestartException rangeRestartException = 66 new RangeRestartException(); 67 private static boolean lockTableDump = false; 68 69 public LockManager(EnvironmentImpl envImpl) 70 throws DatabaseException { 71 72 DbConfigManager configMgr = envImpl.getConfigManager(); 73 nLockTables = configMgr.getInt(EnvironmentParams.N_LOCK_TABLES); 74 lockTables = new Map [nLockTables]; 75 lockTableLatches = new Latch[nLockTables]; 76 for (int i = 0; i < nLockTables; i++) { 77 lockTables[i] = new HashMap (); 78 lockTableLatches[i] = 79 LatchSupport.makeLatch("Lock Table " + i, envImpl); 80 } 81 this.envImpl = envImpl; 82 memoryBudget = envImpl.getMemoryBudget(); 83 nRequests = 0; 84 nWaits = 0; 85 89 90 91 envConfigUpdate(configMgr); 92 envImpl.addConfigObserver(this); 93 } 94 95 98 public void envConfigUpdate(DbConfigManager configMgr) 99 throws DatabaseException { 100 101 LockInfo.setDeadlockStackTrace(configMgr.getBoolean 102 (EnvironmentParams.TXN_DEADLOCK_STACK_TRACE)); 103 setLockTableDump(configMgr.getBoolean 104 (EnvironmentParams.TXN_DUMPLOCKS)); 105 } 106 107 110 static void setLockTableDump(boolean enable) { 111 lockTableDump = enable; 112 } 113 114 protected int getLockTableIndex(Long nodeId) { 115 return ((int) nodeId.longValue()) % 116 nLockTables; 117 } 118 119 protected int getLockTableIndex(long nodeId) { 120 return ((int) nodeId) % nLockTables; 121 } 122 123 160 public LockGrantType lock(long nodeId, 161 Locker locker, 162 LockType type, 163 long timeout, 164 boolean nonBlockingRequest, 165 DatabaseImpl database) 166 throws DeadlockException, DatabaseException { 167 168 assert timeout >= 0; 169 170 174 synchronized (locker) { 175 Long nid = new Long (nodeId); 176 LockAttemptResult result = 177 attemptLock(nid, locker, type, nonBlockingRequest); 178 179 if (result.success || 180 result.lockGrant == LockGrantType.DENIED) { 181 return result.lockGrant; 182 } 183 184 assert checkNoLatchesHeld(nonBlockingRequest): 185 LatchSupport.countLatchesHeld() + 186 " latches held while trying to lock, lock table =" + 187 LatchSupport.latchesHeldToString(); 188 189 194 assert !nonBlockingRequest; 195 try { 196 boolean doWait = true; 197 198 203 if (locker.isTimedOut()) { 204 if (validateOwnership(nid, locker, type, true, 205 memoryBudget)) { 206 doWait = false; 207 } else { 208 String errMsg = 209 makeTimeoutMsg("Transaction", locker, nodeId, type, 210 result.lockGrant, 211 result.useLock, 212 locker.getTxnTimeOut(), 213 locker.getTxnStartMillis(), 214 System.currentTimeMillis(), 215 database); 216 throw new DeadlockException(errMsg); 217 } 218 } 219 220 boolean keepTime = (timeout > 0); 221 long startTime = (keepTime ? System.currentTimeMillis() : 0); 222 while (doWait) { 223 locker.setWaitingFor(result.useLock); 224 try { 225 locker.wait(timeout); 226 } catch (InterruptedException IE) { 227 throw new RunRecoveryException(envImpl, IE); 228 } 229 230 boolean lockerTimedOut = locker.isTimedOut(); 231 long now = System.currentTimeMillis(); 232 boolean thisLockTimedOut = 233 (keepTime && (now - startTime > timeout)); 234 boolean isRestart = 235 (result.lockGrant == LockGrantType.WAIT_RESTART); 236 237 243 if (validateOwnership(nid, locker, type, 244 lockerTimedOut || 245 thisLockTimedOut || 246 isRestart, 247 memoryBudget)) { 248 break; 249 } else { 250 251 254 if (isRestart) { 255 throw rangeRestartException; 256 } 257 258 if (thisLockTimedOut) { 259 locker.setOnlyAbortable(); 260 String errMsg = 261 makeTimeoutMsg("Lock", locker, nodeId, type, 262 result.lockGrant, 263 result.useLock, 264 timeout, startTime, now, 265 database); 266 throw new DeadlockException(errMsg); 267 } 268 269 if (lockerTimedOut) { 270 locker.setOnlyAbortable(); 271 String errMsg = 272 makeTimeoutMsg("Transaction", locker, 273 nodeId, type, 274 result.lockGrant, 275 result.useLock, 276 locker.getTxnTimeOut(), 277 locker.getTxnStartMillis(), 278 now, database); 279 throw new DeadlockException(errMsg); 280 } 281 } 282 } 283 } finally { 284 locker.setWaitingFor(null); 285 assert EnvironmentImpl.maybeForceYield(); 286 } 287 288 locker.addLock(nid, result.useLock, type, result.lockGrant); 289 290 return result.lockGrant; 291 } 292 } 293 294 abstract protected LockAttemptResult 295 attemptLock(Long nodeId, 296 Locker locker, 297 LockType type, 298 boolean nonBlockingRequest) 299 throws DatabaseException; 300 301 protected LockAttemptResult 302 attemptLockInternal(Long nodeId, 303 Locker locker, 304 LockType type, 305 boolean nonBlockingRequest, 306 int lockTableIndex) 307 throws DatabaseException { 308 309 nRequests++; 310 311 312 Map lockTable = lockTables[lockTableIndex]; 313 Lock useLock = (Lock) lockTable.get(nodeId); 314 if (useLock == null) { 315 useLock = new Lock(nodeId); 316 lockTable.put(nodeId, useLock); 317 memoryBudget.updateLockMemoryUsage(TOTAL_LOCK_OVERHEAD, 318 lockTableIndex); 319 } 320 321 325 LockGrantType lockGrant = useLock.lock(type, locker, 326 nonBlockingRequest, 327 memoryBudget, lockTableIndex); 328 boolean success = false; 329 330 331 if ((lockGrant == LockGrantType.NEW) || 332 (lockGrant == LockGrantType.PROMOTION)) { 333 locker.addLock(nodeId, useLock, type, lockGrant); 334 success = true; 335 } else if (lockGrant == LockGrantType.EXISTING) { 336 success = true; 337 } else if (lockGrant == LockGrantType.DENIED) { 338 339 } else { 340 nWaits++; 341 } 342 return new LockAttemptResult(useLock, lockGrant, success); 343 } 344 345 348 protected abstract String makeTimeoutMsg(String lockOrTxn, 349 Locker locker, 350 long nodeId, 351 LockType type, 352 LockGrantType grantType, 353 Lock useLock, 354 long timeout, 355 long start, 356 long now, 357 DatabaseImpl database) 358 throws DatabaseException; 359 360 363 protected String makeTimeoutMsgInternal(String lockOrTxn, 364 Locker locker, 365 long nodeId, 366 LockType type, 367 LockGrantType grantType, 368 Lock useLock, 369 long timeout, 370 long start, 371 long now, 372 DatabaseImpl database) { 373 374 384 if (lockTableDump) { 385 System.out.println("++++++++++ begin lock table dump ++++++++++"); 386 for (int i = 0; i < nLockTables; i++) { 387 StringBuffer sb = new StringBuffer (); 388 dumpToStringNoLatch(sb, i); 389 System.out.println(sb.toString()); 390 } 391 System.out.println("++++++++++ end lock table dump ++++++++++"); 392 } 393 394 StringBuffer sb = new StringBuffer (); 395 sb.append(lockOrTxn); 396 sb.append(" expired. Locker ").append(locker); 397 sb.append(": waited for lock"); 398 399 if (database!=null) { 400 sb.append(" on database=").append(database.getDebugName()); 401 } 402 sb.append(" node=").append(nodeId); 403 sb.append(" type=").append(type); 404 sb.append(" grant=").append(grantType); 405 sb.append(" timeoutMillis=").append(timeout); 406 sb.append(" startTime=").append(start); 407 sb.append(" endTime=").append(now); 408 sb.append("\nOwners: ").append(useLock.getOwnersClone()); 409 sb.append("\nWaiters: ").append(useLock.getWaitersListClone()). 410 append("\n"); 411 StringBuffer deadlockInfo = findDeadlock(useLock, locker); 412 if (deadlockInfo != null) { 413 sb.append(deadlockInfo); 414 } 415 return sb.toString(); 416 } 417 418 427 boolean release(long nodeId, Locker locker) 428 throws DatabaseException { 429 430 return release(nodeId, null, locker, true); 431 } 432 433 442 boolean release(Lock lock, Locker locker) 443 throws DatabaseException { 444 445 return release(-1, lock, locker, false); 446 } 447 448 462 private boolean release(long nodeId, 463 Lock lock, 464 Locker locker, 465 boolean removeFromLocker) 466 throws DatabaseException { 467 468 synchronized (locker) { 469 Set newOwners = 470 releaseAndFindNotifyTargets(nodeId, lock, locker, 471 removeFromLocker); 472 473 if (newOwners == null) { 474 return false; 475 } 476 477 if (newOwners.size() > 0) { 478 479 483 Iterator iter = newOwners.iterator(); 484 485 while (iter.hasNext()) { 486 Locker lockerToNotify = (Locker) iter.next(); 487 488 489 synchronized (lockerToNotify) { 490 lockerToNotify.notifyAll(); 491 } 492 493 assert EnvironmentImpl.maybeForceYield(); 494 } 495 } 496 497 return true; 498 } 499 } 500 501 509 protected abstract Set 510 releaseAndFindNotifyTargets(long nodeId, 511 Lock lock, 512 Locker locker, 513 boolean removeFromLocker) 514 throws DatabaseException; 515 516 519 protected Set 520 releaseAndFindNotifyTargetsInternal(long nodeId, 521 Lock lock, 522 Locker locker, 523 boolean removeFromLocker, 524 int lockTableIndex) 525 throws DatabaseException { 526 527 Lock useLock = lock; 528 529 Map lockTable = lockTables[lockTableIndex]; 530 if (useLock == null) { 531 useLock = (Lock) lockTable.get(new Long (nodeId)); 532 } 533 534 if (useLock == null) { 535 536 return null; 537 } 538 539 Set lockersToNotify = 540 useLock.release(locker, memoryBudget, lockTableIndex); 541 if (lockersToNotify == null) { 542 543 return null; 544 } 545 546 551 if (removeFromLocker) { 552 assert nodeId != -1; 553 locker.removeLock(nodeId, useLock); 554 } 555 556 557 if ((useLock.nWaiters() == 0) && 558 (useLock.nOwners() == 0)) { 559 lockTables[lockTableIndex].remove(useLock.getNodeId()); 560 memoryBudget.updateLockMemoryUsage(REMOVE_TOTAL_LOCK_OVERHEAD, 561 lockTableIndex); 562 } 563 564 return lockersToNotify; 565 } 566 567 572 abstract void transfer(long nodeId, 573 Locker owningLocker, 574 Locker destLocker, 575 boolean demoteToRead) 576 throws DatabaseException; 577 578 581 protected void transferInternal(long nodeId, 582 Locker owningLocker, 583 Locker destLocker, 584 boolean demoteToRead, 585 int lockTableIndex) 586 throws DatabaseException { 587 588 Map lockTable = lockTables[lockTableIndex]; 589 Lock useLock = (Lock) lockTable.get(new Long (nodeId)); 590 591 assert useLock != null : "Transfer, lock " + nodeId + " was null"; 592 if (demoteToRead) { 593 useLock.demote(owningLocker); 594 } 595 useLock.transfer(owningLocker, destLocker, 596 memoryBudget, lockTableIndex); 597 owningLocker.removeLock(nodeId, useLock); 598 } 599 600 607 abstract void transferMultiple(long nodeId, 608 Locker owningLocker, 609 Locker[] destLockers) 610 throws DatabaseException; 611 612 615 protected void transferMultipleInternal(long nodeId, 616 Locker owningLocker, 617 Locker[] destLockers, 618 int lockTableIndex) 619 throws DatabaseException { 620 621 Map lockTable = lockTables[lockTableIndex]; 622 Lock useLock = (Lock) lockTable.get(new Long (nodeId)); 623 624 assert useLock != null : "Transfer, lock " + nodeId + " was null"; 625 useLock.demote(owningLocker); 626 useLock.transferMultiple(owningLocker, destLockers, 627 memoryBudget, lockTableIndex); 628 owningLocker.removeLock(nodeId, useLock); 629 } 630 631 637 abstract void demote(long nodeId, Locker locker) 638 throws DatabaseException; 639 640 643 protected void demoteInternal(long nodeId, 644 Locker locker, 645 int lockTableIndex) 646 throws DatabaseException { 647 648 Map lockTable = lockTables[lockTableIndex]; 649 Lock useLock = (Lock) lockTable.get(new Long (nodeId)); 650 useLock.demote(locker); 651 locker.moveWriteToReadLock(nodeId, useLock); 652 } 653 654 665 abstract boolean isLocked(Long nodeId) 666 throws DatabaseException; 667 668 671 protected boolean isLockedInternal(Long nodeId, int lockTableIndex) { 672 673 Map lockTable = lockTables[lockTableIndex]; 674 Lock entry = (Lock) lockTable.get(nodeId); 675 if (entry == null) { 676 return false; 677 } 678 679 return entry.nOwners() != 0; 680 } 681 682 687 abstract boolean isOwner(Long nodeId, Locker locker, LockType type) 688 throws DatabaseException; 689 690 693 protected boolean isOwnerInternal(Long nodeId, 694 Locker locker, 695 LockType type, 696 int lockTableIndex) { 697 698 Map lockTable = lockTables[lockTableIndex]; 699 Lock entry = (Lock) lockTable.get(nodeId); 700 if (entry == null) { 701 return false; 702 } 703 704 return entry.isOwner(locker, type); 705 } 706 707 712 abstract boolean isWaiter(Long nodeId, Locker locker) 713 throws DatabaseException; 714 715 718 protected boolean isWaiterInternal(Long nodeId, 719 Locker locker, 720 int lockTableIndex) { 721 722 Map lockTable = lockTables[lockTableIndex]; 723 Lock entry = (Lock) lockTable.get(nodeId); 724 if (entry == null) { 725 return false; 726 } 727 728 return entry.isWaiter(locker); 729 } 730 731 734 abstract int nWaiters(Long nodeId) 735 throws DatabaseException; 736 737 740 protected int nWaitersInternal(Long nodeId, int lockTableIndex) { 741 742 Map lockTable = lockTables[lockTableIndex]; 743 Lock entry = (Lock) lockTable.get(nodeId); 744 if (entry == null) { 745 return -1; 746 } 747 748 return entry.nWaiters(); 749 } 750 751 754 abstract int nOwners(Long nodeId) 755 throws DatabaseException; 756 757 760 protected int nOwnersInternal(Long nodeId, int lockTableIndex) { 761 762 Map lockTable = lockTables[lockTableIndex]; 763 Lock entry = (Lock) lockTable.get(nodeId); 764 if (entry == null) { 765 return -1; 766 } 767 768 return entry.nOwners(); 769 } 770 771 774 abstract Locker getWriteOwnerLocker(Long nodeId) 775 throws DatabaseException; 776 777 780 protected Locker getWriteOwnerLockerInternal(Long nodeId, 781 int lockTableIndex) 782 throws DatabaseException { 783 784 Map lockTable = lockTables[lockTableIndex]; 785 Lock lock = (Lock) lockTable.get(nodeId); 786 if (lock == null) { 787 return null; 788 } else if (lock.nOwners() > 1) { 789 790 return null; 791 } else { 792 return lock.getWriteOwnerLocker(); 793 } 794 } 795 796 804 abstract protected boolean validateOwnership(Long nodeId, 805 Locker locker, 806 LockType type, 807 boolean flushFromWaiters, 808 MemoryBudget mb) 809 throws DatabaseException; 810 811 814 protected boolean validateOwnershipInternal(Long nodeId, 815 Locker locker, 816 LockType type, 817 boolean flushFromWaiters, 818 MemoryBudget mb, 819 int lockTableIndex) 820 throws DatabaseException { 821 822 if (isOwnerInternal(nodeId, locker, type, lockTableIndex)) { 823 return true; 824 } 825 826 if (flushFromWaiters) { 827 Lock entry = (Lock) lockTables[lockTableIndex].get(nodeId); 828 if (entry != null) { 829 entry.flushWaiter(locker, mb, lockTableIndex); 830 } 831 } 832 return false; 833 } 834 835 838 public LockStats lockStat(StatsConfig config) 839 throws DatabaseException { 840 841 LockStats stats = new LockStats(); 842 stats.setNRequests(nRequests); 843 stats.setNWaits(nWaits); 844 if (config.getClear()) { 845 nWaits = 0; 846 nRequests = 0; 847 } 848 849 for (int i = 0; i < nLockTables; i++) { 850 LatchStats latchStats = 851 (LatchStats) lockTableLatches[i].getLatchStats(); 852 stats.accumulateLockTableLatchStats(latchStats); 853 } 854 855 856 if (!config.getFast()) { 857 dumpLockTable(stats); 858 } 859 return stats; 860 } 861 862 865 abstract protected void dumpLockTable(LockStats stats) 866 throws DatabaseException; 867 868 871 protected void dumpLockTableInternal(LockStats stats, int i) { 872 Map lockTable = lockTables[i]; 873 stats.accumulateNTotalLocks(lockTable.size()); 874 Iterator iter = lockTable.values().iterator(); 875 while (iter.hasNext()) { 876 Lock lock = (Lock) iter.next(); 877 stats.setNWaiters(stats.getNWaiters() + 878 lock.nWaiters()); 879 stats.setNOwners(stats.getNOwners() + 880 lock.nOwners()); 881 882 883 Iterator ownerIter = lock.getOwnersClone().iterator(); 884 while (ownerIter.hasNext()) { 885 LockInfo info = (LockInfo) ownerIter.next(); 886 if (info.getLockType().isWriteLock()) { 887 stats.setNWriteLocks(stats.getNWriteLocks() + 1); 888 } else { 889 stats.setNReadLocks(stats.getNReadLocks() + 1); 890 } 891 } 892 } 893 } 894 895 898 public void dump() 899 throws DatabaseException { 900 901 System.out.println(dumpToString()); 902 } 903 904 public String dumpToString() 905 throws DatabaseException { 906 907 StringBuffer sb = new StringBuffer (); 908 for (int i = 0; i < nLockTables; i++) { 909 lockTableLatches[i].acquire(); 910 try { 911 dumpToStringNoLatch(sb, i); 912 } finally { 913 lockTableLatches[i].release(); 914 } 915 } 916 return sb.toString(); 917 } 918 919 private void dumpToStringNoLatch(StringBuffer sb, int whichTable) { 920 Map lockTable = lockTables[whichTable]; 921 Iterator entries = lockTable.entrySet().iterator(); 922 923 while (entries.hasNext()) { 924 Map.Entry entry = (Map.Entry ) entries.next(); 925 Long nid = (Long ) entry.getKey(); 926 Lock lock = (Lock) entry.getValue(); 927 sb.append("---- Node Id: ").append(nid).append("----\n"); 928 sb.append(lock); 929 sb.append('\n'); 930 } 931 } 932 933 private boolean checkNoLatchesHeld(boolean nonBlockingRequest) { 934 if (nonBlockingRequest) { 935 return true; } else { 937 return (LatchSupport.countLatchesHeld() == 0); 938 } 939 } 940 941 private StringBuffer findDeadlock(Lock lock, Locker rootLocker) { 942 943 Set ownerSet = new HashSet (); 944 ownerSet.add(rootLocker); 945 StringBuffer ret = findDeadlock1(ownerSet, lock, rootLocker); 946 if (ret != null) { 947 return ret; 948 } else { 949 return null; 950 } 951 } 952 953 private StringBuffer findDeadlock1(Set ownerSet, 954 Lock lock, 955 Locker rootLocker) { 956 957 Iterator ownerIter = lock.getOwnersClone().iterator(); 958 while (ownerIter.hasNext()) { 959 LockInfo info = (LockInfo) ownerIter.next(); 960 Locker locker = info.getLocker(); 961 Lock waitsFor = locker.getWaitingFor(); 962 if (ownerSet.contains(locker) || 963 locker == rootLocker) { 964 965 StringBuffer ret = new StringBuffer (); 966 ret.append("Transaction ").append(locker.toString()); 967 ret.append(" owns ").append(lock.getNodeId()); 968 ret.append(" ").append(info).append("\n"); 969 ret.append("Transaction ").append(locker.toString()); 970 ret.append(" waits for "); 971 if (waitsFor == null) { 972 ret.append(" nothing"); 973 } else { 974 ret.append(" node "); 975 ret.append(waitsFor.getNodeId()); 976 } 977 ret.append("\n"); 978 return ret; 979 } 980 if (waitsFor != null) { 981 ownerSet.add(locker); 982 StringBuffer sb = findDeadlock1(ownerSet, waitsFor, 983 rootLocker); 984 if (sb != null) { 985 String waitInfo = 986 "Transaction " + locker + " waits for node " + 987 waitsFor.getNodeId() + "\n"; 988 sb.insert(0, waitInfo); 989 return sb; 990 } 991 ownerSet.remove(locker); } 993 } 994 995 return null; 996 } 997 998 1001 static class LockAttemptResult { 1002 boolean success; 1003 Lock useLock; 1004 LockGrantType lockGrant; 1005 1006 LockAttemptResult(Lock useLock, 1007 LockGrantType lockGrant, 1008 boolean success) { 1009 1010 this.useLock = useLock; 1011 this.lockGrant = lockGrant; 1012 this.success = success; 1013 } 1014 } 1015} 1016 | Popular Tags |