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