| OLD | NEW |
| 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 // Provide place to put profiling methods for the | 5 // Provide place to put profiling methods for the |
| 6 // Lock class. | 6 // Lock class. |
| 7 // The Lock class is used everywhere, and hence any changes | 7 // The Lock class is used everywhere, and hence any changes |
| 8 // to lock.h tend to require a complete rebuild. To facilitate | 8 // to lock.h tend to require a complete rebuild. To facilitate |
| 9 // profiler development, all the profiling methods are listed | 9 // profiler development, all the profiling methods are listed |
| 10 // here. Note that they are only instantiated in a debug | 10 // here. |
| 11 // build, and the header provides all the trivial implementations | |
| 12 // in a production build. | |
| 13 | 11 |
| 14 #include "base/lock.h" | 12 #include "base/lock.h" |
| 15 #include "base/logging.h" | 13 #include "base/logging.h" |
| 16 | 14 |
| 15 #ifndef NDEBUG |
| 17 Lock::Lock() | 16 Lock::Lock() |
| 18 : lock_(), | 17 : lock_(), |
| 19 recursion_count_shadow_(0) { | 18 recursion_count_shadow_(0), |
| 20 #ifndef NDEBUG | 19 recursion_used_(false), |
| 21 recursion_used_ = false; | 20 acquisition_count_(0), |
| 22 acquisition_count_ = 0; | 21 contention_count_(0) { |
| 23 contention_count_ = 0; | |
| 24 #endif | |
| 25 } | 22 } |
| 23 #else // NDEBUG |
| 24 Lock::Lock() |
| 25 : lock_() { |
| 26 } |
| 27 #endif // NDEBUG |
| 26 | 28 |
| 27 Lock::~Lock() { | 29 Lock::~Lock() { |
| 28 #ifndef NDEBUG | |
| 29 // There should be no one to contend for the lock, | |
| 30 // ...but we need the memory barrier to get a good value. | |
| 31 lock_.Lock(); | |
| 32 int final_recursion_count = recursion_count_shadow_; | |
| 33 lock_.Unlock(); | |
| 34 #endif | |
| 35 | |
| 36 // Allow unit test exception only at end of method. | |
| 37 #ifndef NDEBUG | |
| 38 DCHECK(0 == final_recursion_count); | |
| 39 #endif | |
| 40 } | 30 } |
| 41 | 31 |
| 42 void Lock::Acquire() { | 32 void Lock::Acquire() { |
| 43 #ifdef NDEBUG | 33 #ifdef NDEBUG |
| 44 lock_.Lock(); | 34 lock_.Lock(); |
| 45 recursion_count_shadow_++; | |
| 46 #else // NDEBUG | 35 #else // NDEBUG |
| 47 if (!lock_.Try()) { | 36 if (!lock_.Try()) { |
| 48 // We have contention. | 37 // We have contention. |
| 49 lock_.Lock(); | 38 lock_.Lock(); |
| 50 contention_count_++; | 39 contention_count_++; |
| 51 } | 40 } |
| 52 // ONLY access data after locking. | 41 // ONLY access data after locking. |
| 53 recursion_count_shadow_++; | 42 recursion_count_shadow_++; |
| 54 if (1 == recursion_count_shadow_) | 43 acquisition_count_++; |
| 55 acquisition_count_++; | 44 if (2 == recursion_count_shadow_ && !recursion_used_) { |
| 56 else if (2 == recursion_count_shadow_ && !recursion_used_) | |
| 57 // Usage Note: Set a break point to debug. | |
| 58 recursion_used_ = true; | 45 recursion_used_ = true; |
| 46 // TODO(sky): Uncomment this DCHECK after fixing test cases. |
| 47 // DCHECK(false); // Catch accidental redundant lock acquisition. |
| 48 } |
| 59 #endif // NDEBUG | 49 #endif // NDEBUG |
| 60 } | 50 } |
| 61 | 51 |
| 62 void Lock::Release() { | 52 void Lock::Release() { |
| 53 #ifndef NDEBUG |
| 63 --recursion_count_shadow_; // ONLY access while lock is still held. | 54 --recursion_count_shadow_; // ONLY access while lock is still held. |
| 64 #ifndef NDEBUG | |
| 65 DCHECK(0 <= recursion_count_shadow_); | 55 DCHECK(0 <= recursion_count_shadow_); |
| 66 #endif | 56 #endif // NDEBUG |
| 67 lock_.Unlock(); | 57 lock_.Unlock(); |
| 68 } | 58 } |
| 69 | 59 |
| 70 bool Lock::Try() { | 60 bool Lock::Try() { |
| 71 if (lock_.Try()) { | 61 if (lock_.Try()) { |
| 62 #ifndef NDEBUG |
| 72 recursion_count_shadow_++; | 63 recursion_count_shadow_++; |
| 73 #ifndef NDEBUG | 64 acquisition_count_++; |
| 74 if (1 == recursion_count_shadow_) | 65 if (2 == recursion_count_shadow_ && !recursion_used_) { |
| 75 acquisition_count_++; | |
| 76 else if (2 == recursion_count_shadow_ && !recursion_used_) | |
| 77 // Usage Note: Set a break point to debug. | |
| 78 recursion_used_ = true; | 66 recursion_used_ = true; |
| 67 DCHECK(false); // Catch accidental redundant lock acquisition. |
| 68 } |
| 79 #endif | 69 #endif |
| 80 return true; | 70 return true; |
| 81 } else { | 71 } else { |
| 82 return false; | 72 return false; |
| 83 } | 73 } |
| 84 } | 74 } |
| 85 | |
| 86 // GetCurrentThreadRecursionCount returns the number of nested Acquire() calls | |
| 87 // that have been made by the current thread holding this lock. The calling | |
| 88 // thread is ***REQUIRED*** to be *currently* holding the lock. If that | |
| 89 // calling requirement is violated, the return value is not well defined. | |
| 90 // Return results are guaranteed correct if the caller has acquired this lock. | |
| 91 // The return results might be incorrect otherwise. | |
| 92 // This method is designed to be fast in non-debug mode by co-opting | |
| 93 // synchronization using lock_ (no additional synchronization is used), but in | |
| 94 // debug mode it slowly and carefully validates the requirement (and fires a | |
| 95 // a DCHECK if it was called incorrectly). | |
| 96 int32 Lock::GetCurrentThreadRecursionCount() { | |
| 97 #ifndef NDEBUG | |
| 98 // If this DCHECK fails, then the most probable cause is: | |
| 99 // This method was called by class AutoUnlock during processing of a | |
| 100 // Wait() call made into the ConditonVariable class. That call to | |
| 101 // Wait() was made (incorrectly) without first Aquiring this Lock | |
| 102 // instance. | |
| 103 lock_.Lock(); | |
| 104 int temp = recursion_count_shadow_; | |
| 105 lock_.Unlock(); | |
| 106 // Unit tests catch an exception, so we need to be careful to test | |
| 107 // outside the critical section, since the Leave would be skipped!?! | |
| 108 DCHECK(temp >= 1); // Allow unit test exception only at end of method. | |
| 109 #endif // DEBUG | |
| 110 | |
| 111 // We hold lock, so this *is* correct value. | |
| 112 return recursion_count_shadow_; | |
| 113 } | |
| 114 | |
| 115 | |
| 116 AutoUnlock::AutoUnlock(Lock& lock) : lock_(&lock), release_count_(0) { | |
| 117 // We require our caller have the lock, so we can call for recursion count. | |
| 118 // CRITICALLY: Fetch value before we release the lock. | |
| 119 int32 count = lock_->GetCurrentThreadRecursionCount(); | |
| 120 DCHECK(count > 0); // Make sure we owned the lock. | |
| 121 while (count-- > 0) { | |
| 122 release_count_++; | |
| 123 lock_->Release(); | |
| 124 } | |
| 125 } | |
| 126 | |
| 127 AutoUnlock::~AutoUnlock() { | |
| 128 DCHECK(release_count_ >= 0); | |
| 129 while (release_count_-- > 0) | |
| 130 lock_->Acquire(); | |
| 131 } | |
| 132 | |
| OLD | NEW |