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 |