001 /* 002 * This file is part of the Jikes RVM project (http://jikesrvm.org). 003 * 004 * This file is licensed to You under the Eclipse Public License (EPL); 005 * You may not use this file except in compliance with the License. You 006 * may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/eclipse-1.0.php 009 * 010 * See the COPYRIGHT.txt file distributed with this work for information 011 * regarding copyright ownership. 012 */ 013 package org.jikesrvm.scheduler; 014 015 import org.jikesrvm.VM; 016 import org.jikesrvm.Services; 017 import org.jikesrvm.objectmodel.ThinLockConstants; 018 import org.jikesrvm.runtime.Magic; 019 import org.vmmagic.pragma.Inline; 020 import org.vmmagic.pragma.NoInline; 021 import org.vmmagic.pragma.NoNullCheck; 022 import org.vmmagic.pragma.Uninterruptible; 023 import org.vmmagic.pragma.Unpreemptible; 024 import org.vmmagic.unboxed.Offset; 025 import org.vmmagic.unboxed.Word; 026 027 /** 028 * Implementation of thin locks. 029 */ 030 @Uninterruptible 031 public final class ThinLock implements ThinLockConstants { 032 033 private static final boolean ENABLE_BIASED_LOCKING = true; 034 035 @Inline 036 @NoNullCheck 037 @Unpreemptible 038 public static void inlineLock(Object o, Offset lockOffset) { 039 Word old = Magic.prepareWord(o, lockOffset); // FIXME: bad for PPC? 040 Word id = old.and(TL_THREAD_ID_MASK.or(TL_STAT_MASK)); 041 Word tid = Word.fromIntSignExtend(RVMThread.getCurrentThread().getLockingId()); 042 if (id.EQ(tid)) { 043 Word changed = old.plus(TL_LOCK_COUNT_UNIT); 044 if (!changed.and(TL_LOCK_COUNT_MASK).isZero()) { 045 setDedicatedU16(o, lockOffset, changed); 046 return; 047 } 048 } else if (id.EQ(TL_STAT_THIN)) { 049 // lock is thin and not held by anyone 050 if (Magic.attemptWord(o, lockOffset, old, old.or(tid))) { 051 Magic.isync(); 052 return; 053 } 054 } 055 lock(o, lockOffset); 056 } 057 058 @Inline 059 @NoNullCheck 060 @Unpreemptible 061 public static void inlineUnlock(Object o, Offset lockOffset) { 062 Word old = Magic.prepareWord(o, lockOffset); // FIXME: bad for PPC? 063 Word id = old.and(TL_THREAD_ID_MASK.or(TL_STAT_MASK)); 064 Word tid = Word.fromIntSignExtend(RVMThread.getCurrentThread().getLockingId()); 065 if (id.EQ(tid)) { 066 if (!old.and(TL_LOCK_COUNT_MASK).isZero()) { 067 setDedicatedU16(o, lockOffset, old.minus(TL_LOCK_COUNT_UNIT)); 068 return; 069 } 070 } else if (old.xor(tid).rshl(TL_LOCK_COUNT_SHIFT).EQ(TL_STAT_THIN.rshl(TL_LOCK_COUNT_SHIFT))) { 071 Magic.sync(); 072 if (Magic.attemptWord(o, lockOffset, old, old.and(TL_UNLOCK_MASK).or(TL_STAT_THIN))) { 073 return; 074 } 075 } 076 unlock(o, lockOffset); 077 } 078 079 @NoInline 080 @NoNullCheck 081 @Unpreemptible 082 public static void lock(Object o, Offset lockOffset) { 083 if (STATS) fastLocks++; 084 085 Word threadId = Word.fromIntZeroExtend(RVMThread.getCurrentThread().getLockingId()); 086 087 for (int cnt=0;;cnt++) { 088 Word old = Magic.getWordAtOffset(o, lockOffset); 089 Word stat = old.and(TL_STAT_MASK); 090 boolean tryToInflate=false; 091 if (stat.EQ(TL_STAT_BIASABLE)) { 092 Word id = old.and(TL_THREAD_ID_MASK); 093 if (id.isZero()) { 094 if (ENABLE_BIASED_LOCKING) { 095 // lock is unbiased, bias it in our favor and grab it 096 if (Synchronization.tryCompareAndSwap( 097 o, lockOffset, 098 old, 099 old.or(threadId).plus(TL_LOCK_COUNT_UNIT))) { 100 Magic.isync(); 101 return; 102 } 103 } else { 104 // lock is unbiased but biasing is NOT allowed, so turn it into 105 // a thin lock 106 if (Synchronization.tryCompareAndSwap( 107 o, lockOffset, 108 old, 109 old.or(threadId).or(TL_STAT_THIN))) { 110 Magic.isync(); 111 return; 112 } 113 } 114 } else if (id.EQ(threadId)) { 115 // lock is biased in our favor 116 Word changed = old.plus(TL_LOCK_COUNT_UNIT); 117 if (!changed.and(TL_LOCK_COUNT_MASK).isZero()) { 118 setDedicatedU16(o, lockOffset, changed); 119 return; 120 } else { 121 tryToInflate=true; 122 } 123 } else { 124 if (casFromBiased(o, lockOffset, old, biasBitsToThinBits(old), cnt)) { 125 continue; // don't spin, since it's thin now 126 } 127 } 128 } else if (stat.EQ(TL_STAT_THIN)) { 129 Word id = old.and(TL_THREAD_ID_MASK); 130 if (id.isZero()) { 131 if (Synchronization.tryCompareAndSwap( 132 o, lockOffset, old, old.or(threadId))) { 133 Magic.isync(); 134 return; 135 } 136 } else if (id.EQ(threadId)) { 137 Word changed = old.plus(TL_LOCK_COUNT_UNIT); 138 if (changed.and(TL_LOCK_COUNT_MASK).isZero()) { 139 tryToInflate=true; 140 } else if (Synchronization.tryCompareAndSwap( 141 o, lockOffset, old, changed)) { 142 Magic.isync(); 143 return; 144 } 145 } else if (cnt>retryLimit) { 146 tryToInflate=true; 147 } 148 } else { 149 if (VM.VerifyAssertions) VM._assert(stat.EQ(TL_STAT_FAT)); 150 // lock is fat. contend on it. 151 if (Lock.getLock(getLockIndex(old)).lockHeavy(o)) { 152 return; 153 } 154 } 155 156 if (tryToInflate) { 157 if (STATS) slowLocks++; 158 // the lock is not fat, is owned by someone else, or else the count wrapped. 159 // attempt to inflate it (this may fail, in which case we'll just harmlessly 160 // loop around) and lock it (may also fail, if we get the wrong lock). if it 161 // succeeds, we're done. 162 // NB: this calls into our attemptToMarkInflated() method, which will do the 163 // Right Thing if the lock is biased to someone else. 164 if (inflateAndLock(o, lockOffset)) { 165 return; 166 } 167 } else { 168 RVMThread.yieldNoHandshake(); 169 } 170 } 171 } 172 173 @NoInline 174 @NoNullCheck 175 @Unpreemptible 176 public static void unlock(Object o, Offset lockOffset) { 177 Word threadId = Word.fromIntZeroExtend(RVMThread.getCurrentThread().getLockingId()); 178 for (int cnt=0;;cnt++) { 179 Word old = Magic.getWordAtOffset(o, lockOffset); 180 Word stat = old.and(TL_STAT_MASK); 181 if (stat.EQ(TL_STAT_BIASABLE)) { 182 Word id = old.and(TL_THREAD_ID_MASK); 183 if (id.EQ(threadId)) { 184 if (old.and(TL_LOCK_COUNT_MASK).isZero()) { 185 RVMThread.raiseIllegalMonitorStateException("biased unlocking: we own this object but the count is already zero", o); 186 } 187 setDedicatedU16(o, lockOffset, old.minus(TL_LOCK_COUNT_UNIT)); 188 return; 189 } else { 190 RVMThread.raiseIllegalMonitorStateException("biased unlocking: we don't own this object", o); 191 } 192 } else if (stat.EQ(TL_STAT_THIN)) { 193 Magic.sync(); 194 Word id = old.and(TL_THREAD_ID_MASK); 195 if (id.EQ(threadId)) { 196 Word changed; 197 if (old.and(TL_LOCK_COUNT_MASK).isZero()) { 198 changed = old.and(TL_UNLOCK_MASK).or(TL_STAT_THIN); 199 } else { 200 changed = old.minus(TL_LOCK_COUNT_UNIT); 201 } 202 if (Synchronization.tryCompareAndSwap( 203 o, lockOffset, old, changed)) { 204 return; 205 } 206 } else { 207 if (false) { 208 VM.sysWriteln("threadId = ",threadId); 209 VM.sysWriteln("id = ",id); 210 } 211 RVMThread.raiseIllegalMonitorStateException("thin unlocking: we don't own this object", o); 212 } 213 } else { 214 if (VM.VerifyAssertions) VM._assert(stat.EQ(TL_STAT_FAT)); 215 // fat unlock 216 Lock.getLock(getLockIndex(old)).unlockHeavy(o); 217 return; 218 } 219 } 220 } 221 222 @Uninterruptible 223 @NoNullCheck 224 public static boolean holdsLock(Object o, Offset lockOffset, RVMThread thread) { 225 for (int cnt=0;;++cnt) { 226 int tid = thread.getLockingId(); 227 Word bits = Magic.getWordAtOffset(o, lockOffset); 228 if (bits.and(TL_STAT_MASK).EQ(TL_STAT_BIASABLE)) { 229 // if locked, then it is locked with a thin lock 230 return 231 bits.and(TL_THREAD_ID_MASK).toInt() == tid && 232 !bits.and(TL_LOCK_COUNT_MASK).isZero(); 233 } else if (bits.and(TL_STAT_MASK).EQ(TL_STAT_THIN)) { 234 return bits.and(TL_THREAD_ID_MASK).toInt()==tid; 235 } else { 236 if (VM.VerifyAssertions) VM._assert(bits.and(TL_STAT_MASK).EQ(TL_STAT_FAT)); 237 // if locked, then it is locked with a fat lock 238 Lock l=Lock.getLock(getLockIndex(bits)); 239 if (l!=null) { 240 l.mutex.lock(); 241 boolean result = (l.getOwnerId()==tid && l.getLockedObject()==o); 242 l.mutex.unlock(); 243 return result; 244 } 245 } 246 RVMThread.yieldNoHandshake(); 247 } 248 } 249 250 @Inline 251 @Uninterruptible 252 public static boolean isFat(Word lockWord) { 253 return lockWord.and(TL_STAT_MASK).EQ(TL_STAT_FAT); 254 } 255 256 /** 257 * Return the lock index for a given lock word. Assert valid index 258 * ranges, that the fat lock bit is set, and that the lock entry 259 * exists. 260 * 261 * @param lockWord The lock word whose lock index is being established 262 * @return the lock index corresponding to the lock workd. 263 */ 264 @Inline 265 @Uninterruptible 266 public static int getLockIndex(Word lockWord) { 267 int index = lockWord.and(TL_LOCK_ID_MASK).rshl(TL_LOCK_ID_SHIFT).toInt(); 268 if (VM.VerifyAssertions) { 269 if (!(index > 0 && index < Lock.numLocks())) { 270 VM.sysWrite("Lock index out of range! Word: "); VM.sysWrite(lockWord); 271 VM.sysWrite(" index: "); VM.sysWrite(index); 272 VM.sysWrite(" locks: "); VM.sysWrite(Lock.numLocks()); 273 VM.sysWriteln(); 274 } 275 VM._assert(index > 0 && index < Lock.numLocks()); // index is in range 276 VM._assert(lockWord.and(TL_STAT_MASK).EQ(TL_STAT_FAT)); // fat lock bit is set 277 } 278 return index; 279 } 280 281 @Inline 282 @Uninterruptible 283 public static int getLockOwner(Word lockWord) { 284 if (VM.VerifyAssertions) VM._assert(!isFat(lockWord)); 285 if (lockWord.and(TL_STAT_MASK).EQ(TL_STAT_BIASABLE)) { 286 if (lockWord.and(TL_LOCK_COUNT_MASK).isZero()) { 287 return 0; 288 } else { 289 return lockWord.and(TL_THREAD_ID_MASK).toInt(); 290 } 291 } else { 292 return lockWord.and(TL_THREAD_ID_MASK).toInt(); 293 } 294 } 295 296 @Inline 297 @Uninterruptible 298 public static int getRecCount(Word lockWord) { 299 if (VM.VerifyAssertions) VM._assert(getLockOwner(lockWord)!=0); 300 if (lockWord.and(TL_STAT_MASK).EQ(TL_STAT_BIASABLE)) { 301 return lockWord.and(TL_LOCK_COUNT_MASK).rshl(TL_LOCK_COUNT_SHIFT).toInt(); 302 } else { 303 return lockWord.and(TL_LOCK_COUNT_MASK).rshl(TL_LOCK_COUNT_SHIFT).toInt()+1; 304 } 305 } 306 307 /** 308 * Set only the dedicated locking 16-bit part of the given value. This is the only part 309 * that is allowed to be written without a CAS. This takes care of the shifting and 310 * storing of the value. 311 * 312 * @param o The object whose header is to be changed 313 * @param lockOffset The lock offset 314 * @param value The value which contains the 16-bit portion to be written. 315 */ 316 @Inline 317 @Unpreemptible 318 private static void setDedicatedU16(Object o, Offset lockOffset, Word value) { 319 Magic.setCharAtOffset(o, lockOffset.plus(TL_DEDICATED_U16_OFFSET), (char)(value.toInt() >>> TL_DEDICATED_U16_SHIFT)); 320 } 321 322 @NoInline 323 @Unpreemptible 324 public static boolean casFromBiased(Object o, Offset lockOffset, 325 Word oldLockWord, Word changed, 326 int cnt) { 327 RVMThread me=RVMThread.getCurrentThread(); 328 Word id=oldLockWord.and(TL_THREAD_ID_MASK); 329 if (id.isZero()) { 330 if (false) VM.sysWriteln("id is zero - easy case."); 331 return Synchronization.tryCompareAndSwap(o, lockOffset, oldLockWord, changed); 332 } else { 333 if (false) VM.sysWriteln("id = ",id); 334 int slot=id.toInt()>>TL_THREAD_ID_SHIFT; 335 if (false) VM.sysWriteln("slot = ",slot); 336 RVMThread owner=RVMThread.threadBySlot[slot]; 337 if (owner==me /* I own it, so I can unbias it trivially. This occurs 338 when we are inflating due to, for example, wait() */ || 339 owner==null /* the thread that owned it is dead, so it's safe to 340 unbias. */) { 341 // note that we use a CAS here, but it's only needed in the case 342 // that owner==null, since in that case some other thread may also 343 // be unbiasing. 344 return Synchronization.tryCompareAndSwap( 345 o, lockOffset, oldLockWord, changed); 346 } else { 347 boolean result=false; 348 349 // NB. this may stop a thread other than the one that had the bias, 350 // if that thread died and some other thread took its slot. that's 351 // why we do a CAS below. it's only needed if some other thread 352 // had seen the owner be null (which may happen if we came here after 353 // a new thread took the slot while someone else came here when the 354 // slot was still null). if it was the case that everyone else had 355 // seen a non-null owner, then the pair handshake would serve as 356 // sufficient synchronization (the id would identify the set of threads 357 // that shared that id's communicationLock). oddly, that means that 358 // this whole thing could be "simplified" to acquire the 359 // communicationLock even if the owner was null. but that would be 360 // goofy. 361 if (false) VM.sysWriteln("entering pair handshake"); 362 owner.beginPairHandshake(); 363 if (false) VM.sysWriteln("done with that"); 364 365 Word newLockWord=Magic.getWordAtOffset(o, lockOffset); 366 result=Synchronization.tryCompareAndSwap( 367 o, lockOffset, oldLockWord, changed); 368 owner.endPairHandshake(); 369 if (false) VM.sysWriteln("that worked."); 370 371 return result; 372 } 373 } 374 } 375 376 @Inline 377 @Unpreemptible 378 public static boolean attemptToMarkInflated(Object o, Offset lockOffset, 379 Word oldLockWord, 380 int lockId, 381 int cnt) { 382 if (VM.VerifyAssertions) VM._assert(oldLockWord.and(TL_STAT_MASK).NE(TL_STAT_FAT)); 383 if (false) VM.sysWriteln("attemptToMarkInflated with oldLockWord = ",oldLockWord); 384 // what this needs to do: 385 // 1) if the lock is thin, it's just a CAS 386 // 2) if the lock is unbiased, CAS in the inflation 387 // 3) if the lock is biased in our favor, store the lock without CAS 388 // 4) if the lock is biased but to someone else, enter the pair handshake 389 // to unbias it and install the inflated lock 390 Word changed= 391 TL_STAT_FAT.or(Word.fromIntZeroExtend(lockId).lsh(TL_LOCK_ID_SHIFT)) 392 .or(oldLockWord.and(TL_UNLOCK_MASK)); 393 if (false && oldLockWord.and(TL_STAT_MASK).EQ(TL_STAT_THIN)) 394 VM.sysWriteln("obj = ",Magic.objectAsAddress(o), 395 ", old = ",oldLockWord, 396 ", owner = ",getLockOwner(oldLockWord), 397 ", rec = ",getLockOwner(oldLockWord)==0?0:getRecCount(oldLockWord), 398 ", changed = ",changed, 399 ", lockId = ",lockId); 400 if (false) VM.sysWriteln("changed = ",changed); 401 if (oldLockWord.and(TL_STAT_MASK).EQ(TL_STAT_THIN)) { 402 if (false) VM.sysWriteln("it's thin, inflating the easy way."); 403 return Synchronization.tryCompareAndSwap( 404 o, lockOffset, oldLockWord, changed); 405 } else { 406 return casFromBiased(o, lockOffset, oldLockWord, changed, cnt); 407 } 408 } 409 410 /** 411 * Promotes a light-weight lock to a heavy-weight lock. If this returns the lock 412 * that you gave it, its mutex will be locked; otherwise, its mutex will be unlocked. 413 * Hence, calls to this method should always be followed by a condition lock() or 414 * unlock() call. 415 * 416 * @param o the object to get a heavy-weight lock 417 * @param lockOffset the offset of the thin lock word in the object. 418 * @return the inflated lock; either the one you gave, or another one, if the lock 419 * was inflated by some other thread. 420 */ 421 @NoNullCheck 422 @Unpreemptible 423 protected static Lock attemptToInflate(Object o, 424 Offset lockOffset, 425 Lock l) { 426 if (false) VM.sysWriteln("l = ",Magic.objectAsAddress(l)); 427 l.mutex.lock(); 428 for (int cnt=0;;++cnt) { 429 Word bits = Magic.getWordAtOffset(o, lockOffset); 430 // check to see if another thread has already created a fat lock 431 if (isFat(bits)) { 432 if (trace) { 433 VM.sysWriteln("Thread #",RVMThread.getCurrentThreadSlot(), 434 ": freeing lock ",Magic.objectAsAddress(l), 435 " because we had a double-inflate"); 436 } 437 Lock result = Lock.getLock(getLockIndex(bits)); 438 if (result==null || 439 result.lockedObject!=o) { 440 continue; /* this is nasty. this will happen when a lock 441 is deflated. */ 442 } 443 Lock.free(l); 444 l.mutex.unlock(); 445 return result; 446 } 447 if (VM.VerifyAssertions) VM._assert(l!=null); 448 if (attemptToMarkInflated( 449 o, lockOffset, bits, l.index, cnt)) { 450 l.setLockedObject(o); 451 l.setOwnerId(getLockOwner(bits)); 452 if (l.getOwnerId() != 0) { 453 l.setRecursionCount(getRecCount(bits)); 454 } else { 455 if (VM.VerifyAssertions) VM._assert(l.getRecursionCount()==0); 456 } 457 return l; 458 } 459 // contention detected, try again 460 } 461 } 462 463 @Inline 464 @Uninterruptible 465 private static Word biasBitsToThinBits(Word bits) { 466 int lockOwner=getLockOwner(bits); 467 468 Word changed=bits.and(TL_UNLOCK_MASK).or(TL_STAT_THIN); 469 470 if (lockOwner!=0) { 471 int recCount=getRecCount(bits); 472 changed=changed 473 .or(Word.fromIntZeroExtend(lockOwner)) 474 .or(Word.fromIntZeroExtend(recCount-1).lsh(TL_LOCK_COUNT_SHIFT)); 475 } 476 477 return changed; 478 } 479 480 @Inline 481 @Uninterruptible 482 public static boolean attemptToMarkDeflated(Object o, Offset lockOffset, 483 Word oldLockWord) { 484 // we allow concurrent modification of the lock word when it's thin or fat. 485 Word changed=oldLockWord.and(TL_UNLOCK_MASK).or(TL_STAT_THIN); 486 if (VM.VerifyAssertions) VM._assert(getLockOwner(changed)==0); 487 return Synchronization.tryCompareAndSwap( 488 o, lockOffset, oldLockWord, changed); 489 } 490 491 @Uninterruptible 492 public static void markDeflated(Object o, Offset lockOffset, int id) { 493 for (;;) { 494 Word bits=Magic.getWordAtOffset(o, lockOffset); 495 if (VM.VerifyAssertions) VM._assert(isFat(bits)); 496 if (VM.VerifyAssertions) VM._assert(getLockIndex(bits)==id); 497 if (attemptToMarkDeflated(o, lockOffset, bits)) { 498 return; 499 } 500 } 501 } 502 503 //////////////////////////////////////////////////////////////// 504 /// Support for inflating (and deflating) heavy-weight locks /// 505 //////////////////////////////////////////////////////////////// 506 507 /** 508 * Promotes a light-weight lock to a heavy-weight lock. Note: the 509 * object is question will normally be locked by another thread, 510 * or it may be unlocked. If there is already a heavy-weight lock 511 * on this object, that lock is returned. 512 * 513 * @param o the object to get a heavy-weight lock 514 * @param lockOffset the offset of the thin lock word in the object. 515 * @return the heavy-weight lock on this object 516 */ 517 @Unpreemptible 518 private static Lock inflate(Object o, Offset lockOffset) { 519 Lock l = Lock.allocate(); 520 if (VM.VerifyAssertions) { 521 VM._assert(l != null); // inflate called by wait (or notify) which shouldn't be called during GC 522 } 523 Lock rtn = attemptToInflate(o, lockOffset, l); 524 if (rtn == l) 525 l.mutex.unlock(); 526 return rtn; 527 } 528 529 /** 530 * Promotes a light-weight lock to a heavy-weight lock and locks it. 531 * Note: the object in question will normally be locked by another 532 * thread, or it may be unlocked. If there is already a 533 * heavy-weight lock on this object, that lock is returned. 534 * 535 * @param o the object to get a heavy-weight lock 536 * @param lockOffset the offset of the thin lock word in the object. 537 * @return whether the object was successfully locked 538 */ 539 @Unpreemptible 540 private static boolean inflateAndLock(Object o, Offset lockOffset) { 541 Lock l = Lock.allocate(); 542 if (l == null) return false; // can't allocate locks during GC 543 Lock rtn = attemptToInflate(o, lockOffset, l); 544 if (l != rtn) { 545 l = rtn; 546 l.mutex.lock(); 547 } 548 return l.lockHeavyLocked(o); 549 } 550 551 //////////////////////////////////////////////////////////////////////////// 552 /// Get heavy-weight lock for an object; if thin, inflate it. 553 //////////////////////////////////////////////////////////////////////////// 554 555 /** 556 * Obtains the heavy-weight lock, if there is one, associated with the 557 * indicated object. Returns <code>null</code>, if there is no 558 * heavy-weight lock associated with the object. 559 * 560 * @param o the object from which a lock is desired 561 * @param lockOffset the offset of the thin lock word in the object. 562 * @param create if true, create heavy lock if none found 563 * @return the heavy-weight lock on the object (if any) 564 */ 565 @Unpreemptible 566 public static Lock getHeavyLock(Object o, Offset lockOffset, boolean create) { 567 Word old = Magic.getWordAtOffset(o, lockOffset); 568 if (isFat(old)) { // already a fat lock in place 569 return Lock.getLock(getLockIndex(old)); 570 } else if (create) { 571 return inflate(o, lockOffset); 572 } else { 573 return null; 574 } 575 } 576 577 /////////////////////////////////////////////////////////////// 578 /// Support for debugging and performance tuning /// 579 /////////////////////////////////////////////////////////////// 580 581 /** 582 * Number of times a thread yields before inflating the lock on a 583 * object to a heavy-weight lock. The current value was for the 584 * portBOB benchmark on a 12-way SMP (AIX) in the Fall of '99. FP 585 * confirmed that it's still optimal for JBB and DaCapo on 4-, 8-, 586 * and 16-way SMPs (Linux/ia32) in Spring '09. 587 */ 588 private static final int retryLimit = 40; 589 590 static final boolean STATS = Lock.STATS; 591 592 static final boolean trace = false; 593 594 static int fastLocks; 595 static int slowLocks; 596 597 static void notifyAppRunStart(String app, int value) { 598 if (!STATS) return; 599 fastLocks = 0; 600 slowLocks = 0; 601 } 602 603 static void notifyExit(int value) { 604 if (!STATS) return; 605 VM.sysWrite("ThinLocks: "); 606 VM.sysWrite(fastLocks); 607 VM.sysWrite(" fast locks"); 608 Services.percentage(fastLocks, value, "all lock operations"); 609 VM.sysWrite("ThinLocks: "); 610 VM.sysWrite(slowLocks); 611 VM.sysWrite(" slow locks"); 612 Services.percentage(slowLocks, value, "all lock operations"); 613 } 614 615 } 616