/Users/deen/code/yugabyte-db/src/yb/gutil/spinlock.h
Line | Count | Source |
1 | | /* Copyright (c) 2006, Google Inc. |
2 | | * All rights reserved. |
3 | | * |
4 | | * Redistribution and use in source and binary forms, with or without |
5 | | * modification, are permitted provided that the following conditions are |
6 | | * met: |
7 | | * |
8 | | * * Redistributions of source code must retain the above copyright |
9 | | * notice, this list of conditions and the following disclaimer. |
10 | | * * Redistributions in binary form must reproduce the above |
11 | | * copyright notice, this list of conditions and the following disclaimer |
12 | | * in the documentation and/or other materials provided with the |
13 | | * distribution. |
14 | | * * Neither the name of Google Inc. nor the names of its |
15 | | * contributors may be used to endorse or promote products derived from |
16 | | * this software without specific prior written permission. |
17 | | * |
18 | | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
19 | | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
20 | | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
21 | | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
22 | | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
23 | | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
24 | | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
25 | | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
26 | | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
27 | | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
28 | | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
29 | | * |
30 | | * The following only applies to changes made to this file as part of YugaByte development. |
31 | | * |
32 | | * Portions Copyright (c) YugaByte, Inc. |
33 | | * |
34 | | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
35 | | * in compliance with the License. You may obtain a copy of the License at |
36 | | * |
37 | | * http://www.apache.org/licenses/LICENSE-2.0 |
38 | | * |
39 | | * Unless required by applicable law or agreed to in writing, software distributed under the License |
40 | | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
41 | | * or implied. See the License for the specific language governing permissions and limitations |
42 | | * under the License. |
43 | | * |
44 | | * |
45 | | * --- |
46 | | * Author: Sanjay Ghemawat |
47 | | */ |
48 | | |
49 | | // SpinLock is async signal safe. |
50 | | // If used within a signal handler, all lock holders |
51 | | // should block the signal even outside the signal handler. |
52 | | |
53 | | #ifndef YB_GUTIL_SPINLOCK_H |
54 | | #define YB_GUTIL_SPINLOCK_H |
55 | | |
56 | | #include "yb/gutil/atomicops.h" |
57 | | #include "yb/gutil/dynamic_annotations.h" |
58 | | #include "yb/gutil/macros.h" |
59 | | #include "yb/gutil/thread_annotations.h" |
60 | | |
61 | | // This isn't originally in the base:: namespace in tcmalloc, |
62 | | // but tcmalloc inadvertently exports these symbols. So, if we |
63 | | // don't namespace it differently, we conflict. |
64 | | namespace base { |
65 | | |
66 | | class CAPABILITY("mutex") SpinLock { |
67 | | public: |
68 | 496M | SpinLock() : lockword_(kSpinLockFree) { } |
69 | | |
70 | | // Special constructor for use with static SpinLock objects. E.g., |
71 | | // |
72 | | // static SpinLock lock(base::LINKER_INITIALIZED); |
73 | | // |
74 | | // When intialized using this constructor, we depend on the fact |
75 | | // that the linker has already initialized the memory appropriately. |
76 | | // A SpinLock constructed like this can be freely used from global |
77 | | // initializers without worrying about the order in which global |
78 | | // initializers run. |
79 | | explicit SpinLock(base::LinkerInitialized /*x*/) |
80 | 59.5k | : lockword_(kSpinLockFree) { |
81 | 59.5k | } |
82 | | |
83 | | // Acquire this SpinLock. |
84 | | // TODO(csilvers): uncomment the annotation when we figure out how to |
85 | | // support this macro with 0 args (see thread_annotations.h) |
86 | 4.41G | inline void Lock() ACQUIRE() { |
87 | 4.41G | if (base::subtle::Acquire_CompareAndSwap(&lockword_, kSpinLockFree, |
88 | 4.41G | kSpinLockHeld) != kSpinLockFree) { |
89 | 28.1M | SlowLock(); |
90 | 28.1M | } |
91 | 4.41G | ANNOTATE_RWLOCK_ACQUIRED(this, 1); |
92 | 4.41G | #ifdef __aarch64__ |
93 | 4.41G | __asm__ __volatile__ ("dmb ish" ::: "memory"); |
94 | 4.41G | #endif |
95 | 4.41G | } |
96 | | |
97 | | // Try to acquire this SpinLock without blocking and return true if the |
98 | | // acquisition was successful. If the lock was not acquired, false is |
99 | | // returned. If this SpinLock is free at the time of the call, TryLock |
100 | | // will return true with high probability. |
101 | 13.4k | inline bool TryLock() TRY_ACQUIRE(true) { |
102 | 13.4k | bool res = |
103 | 13.4k | (base::subtle::Acquire_CompareAndSwap(&lockword_, kSpinLockFree, |
104 | 13.4k | kSpinLockHeld) == kSpinLockFree); |
105 | 13.4k | if (res) { |
106 | 13.4k | ANNOTATE_RWLOCK_ACQUIRED(this, 1); |
107 | 13.4k | } |
108 | 13.4k | #ifdef __aarch64__ |
109 | 13.4k | __asm__ __volatile__ ("dmb ish" ::: "memory"); |
110 | 13.4k | #endif |
111 | 13.4k | return res; |
112 | 13.4k | } |
113 | | |
114 | | // Release this SpinLock, which must be held by the calling thread. |
115 | | // TODO(csilvers): uncomment the annotation when we figure out how to |
116 | | // support this macro with 0 args (see thread_annotations.h) |
117 | 4.41G | inline void Unlock() RELEASE() { |
118 | 4.41G | ANNOTATE_RWLOCK_RELEASED(this, 1); |
119 | 4.41G | uint64 wait_cycles = static_cast<uint64>( |
120 | 4.41G | base::subtle::Release_AtomicExchange(&lockword_, kSpinLockFree)); |
121 | 4.41G | if (wait_cycles != kSpinLockHeld) { |
122 | | // Collect contentionz profile info, and speed the wakeup of any waiter. |
123 | | // The wait_cycles value indicates how long this thread spent waiting |
124 | | // for the lock. |
125 | 38.0M | SlowUnlock(wait_cycles); |
126 | 38.0M | } |
127 | 4.41G | #ifdef __aarch64__ |
128 | 4.41G | __asm__ __volatile__ ("dmb ish" ::: "memory"); |
129 | 4.41G | #endif |
130 | 4.41G | } |
131 | | |
132 | | // Determine if the lock is held. When the lock is held by the invoking |
133 | | // thread, true will always be returned. Intended to be used as |
134 | | // CHECK(lock.IsHeld()). |
135 | 244M | inline bool IsHeld() const { |
136 | 244M | return base::subtle::NoBarrier_Load(&lockword_) != kSpinLockFree; |
137 | 244M | } |
138 | | |
139 | | static const base::LinkerInitialized LINKER_INITIALIZED; // backwards compat |
140 | | private: |
141 | | enum { kSpinLockFree = 0 }; |
142 | | enum { kSpinLockHeld = 1 }; |
143 | | enum { kSpinLockSleeper = 2 }; |
144 | | |
145 | | volatile Atomic32 lockword_; |
146 | | |
147 | | void SlowLock(); |
148 | | void SlowUnlock(uint64 wait_cycles); |
149 | | Atomic32 SpinLoop(int64 initial_wait_timestamp, Atomic32* wait_cycles); |
150 | | inline int32 CalculateWaitCycles(int64 wait_start_time); |
151 | | |
152 | | DISALLOW_COPY_AND_ASSIGN(SpinLock); |
153 | | }; |
154 | | |
155 | | // Corresponding locker object that arranges to acquire a spinlock for |
156 | | // the duration of a C++ scope. |
157 | | class SCOPED_CAPABILITY SpinLockHolder { |
158 | | private: |
159 | | SpinLock* lock_; |
160 | | public: |
161 | | inline explicit SpinLockHolder(SpinLock* l) ACQUIRE(l) |
162 | 784k | : lock_(l) { |
163 | 784k | l->Lock(); |
164 | 784k | } |
165 | | |
166 | 785k | inline ~SpinLockHolder() RELEASE() { lock_->Unlock(); } |
167 | | }; |
168 | | // Catch bug where variable name is omitted, e.g. SpinLockHolder (&lock); |
169 | | #define SpinLockHolder(x) COMPILE_ASSERT(0, spin_lock_decl_missing_var_name) |
170 | | |
171 | | } // namespace base |
172 | | |
173 | | #endif // YB_GUTIL_SPINLOCK_H |