/Users/deen/code/yugabyte-db/src/yb/util/rw_semaphore.h
Line | Count | Source (jump to first uncovered line) |
1 | | // Licensed to the Apache Software Foundation (ASF) under one |
2 | | // or more contributor license agreements. See the NOTICE file |
3 | | // distributed with this work for additional information |
4 | | // regarding copyright ownership. The ASF licenses this file |
5 | | // to you under the Apache License, Version 2.0 (the |
6 | | // "License"); you may not use this file except in compliance |
7 | | // with the License. You may obtain a copy of the License at |
8 | | // |
9 | | // http://www.apache.org/licenses/LICENSE-2.0 |
10 | | // |
11 | | // Unless required by applicable law or agreed to in writing, |
12 | | // software distributed under the License is distributed on an |
13 | | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
14 | | // KIND, either express or implied. See the License for the |
15 | | // specific language governing permissions and limitations |
16 | | // under the License. |
17 | | // |
18 | | // The following only applies to changes made to this file as part of YugaByte development. |
19 | | // |
20 | | // Portions Copyright (c) YugaByte, Inc. |
21 | | // |
22 | | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
23 | | // in compliance with the License. You may obtain a copy of the License at |
24 | | // |
25 | | // http://www.apache.org/licenses/LICENSE-2.0 |
26 | | // |
27 | | // Unless required by applicable law or agreed to in writing, software distributed under the License |
28 | | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
29 | | // or implied. See the License for the specific language governing permissions and limitations |
30 | | // under the License. |
31 | | // |
32 | | #ifndef YB_UTIL_RW_SEMAPHORE_H |
33 | | #define YB_UTIL_RW_SEMAPHORE_H |
34 | | |
35 | | #include <boost/smart_ptr/detail/yield_k.hpp> |
36 | | #include <glog/logging.h> |
37 | | |
38 | | #include "yb/gutil/atomicops.h" |
39 | | #include "yb/gutil/port.h" |
40 | | |
41 | | namespace yb { |
42 | | |
43 | | // Read-Write semaphore. 32bit uint that contains the number of readers. |
44 | | // When someone wants to write, tries to set the 32bit, and waits until |
45 | | // the readers have finished. Readers are spinning while the write flag is set. |
46 | | // |
47 | | // This rw-semaphore makes no attempt at fairness, though it does avoid write |
48 | | // starvation (no new readers may obtain the lock if a write is waiting). |
49 | | // |
50 | | // Given that this is currently based only on spinning (and not futex), |
51 | | // it should only be used in cases where the lock is held for very short |
52 | | // time intervals. |
53 | | // |
54 | | // If the semaphore is expected to always be released from the same thread |
55 | | // that acquired it, use rw_spinlock instead. |
56 | | // |
57 | | // In order to support easier debugging of leaked locks, this class can track |
58 | | // the stack trace of the last thread to lock it in write mode. To do so, |
59 | | // uncomment the following define: |
60 | | // #define RW_SEMAPHORE_TRACK_HOLDER 1 |
61 | | // ... and then in gdb, print the contents of the semaphore, and you should |
62 | | // see the collected stack trace. |
63 | | class rw_semaphore { |
64 | | #ifndef NDEBUG |
65 | | static constexpr int64_t kInvalidThreadId = -1; |
66 | | #endif |
67 | | public: |
68 | 1.92M | rw_semaphore() : state_(0) {} |
69 | 753k | ~rw_semaphore() {} |
70 | | |
71 | 220M | void lock_shared() { |
72 | 220M | int loop_count = 0; |
73 | 220M | Atomic32 cur_state = base::subtle::NoBarrier_Load(&state_); |
74 | 331M | while (true) { |
75 | 331M | Atomic32 expected = cur_state & kNumReadersMask; // I expect no write lock |
76 | 331M | Atomic32 try_new_state = expected + 1; // Add me as reader |
77 | 331M | cur_state = base::subtle::Acquire_CompareAndSwap(&state_, expected, try_new_state); |
78 | 331M | if (cur_state == expected) |
79 | 220M | break; |
80 | | // Either was already locked by someone else, or CAS failed. |
81 | 111M | boost::detail::yield(loop_count++); |
82 | 111M | } |
83 | 220M | } |
84 | | |
85 | 220M | void unlock_shared() { |
86 | 220M | int loop_count = 0; |
87 | 220M | Atomic32 cur_state = base::subtle::NoBarrier_Load(&state_); |
88 | 220M | while (true) { |
89 | 0 | DCHECK_GT(cur_state & kNumReadersMask, 0) |
90 | 0 | << "unlock_shared() called when there are no shared locks held"; |
91 | 220M | Atomic32 expected = cur_state; // I expect a write lock and other readers |
92 | 220M | Atomic32 try_new_state = expected - 1; // Drop me as reader |
93 | 220M | cur_state = base::subtle::Release_CompareAndSwap(&state_, expected, try_new_state); |
94 | 220M | if (cur_state == expected) |
95 | 220M | break; |
96 | | // Either was already locked by someone else, or CAS failed. |
97 | 209k | boost::detail::yield(loop_count++); |
98 | 209k | } |
99 | 220M | } |
100 | | |
101 | | // Tries to acquire a write lock, if no one else has it. |
102 | | // This function retries on CAS failure and waits for readers to complete. |
103 | 0 | bool try_lock() { |
104 | 0 | int loop_count = 0; |
105 | 0 | Atomic32 cur_state = base::subtle::NoBarrier_Load(&state_); |
106 | 0 | while (true) { |
107 | 0 | // someone else has already the write lock |
108 | 0 | if (cur_state & kWriteFlag) |
109 | 0 | return false; |
110 | 0 |
|
111 | 0 | Atomic32 expected = cur_state & kNumReadersMask; // I expect some 0+ readers |
112 | 0 | Atomic32 try_new_state = kWriteFlag | expected; // I want to lock the other writers |
113 | 0 | cur_state = base::subtle::Acquire_CompareAndSwap(&state_, expected, try_new_state); |
114 | 0 | if (cur_state == expected) |
115 | 0 | break; |
116 | 0 | // Either was already locked by someone else, or CAS failed. |
117 | 0 | boost::detail::yield(loop_count++); |
118 | 0 | } |
119 | 0 |
|
120 | 0 | WaitPendingReaders(); |
121 | 0 | RecordLockHolderStack(); |
122 | 0 | return true; |
123 | 0 | } |
124 | | |
125 | 31.4M | void lock() { |
126 | 31.4M | int loop_count = 0; |
127 | 31.4M | Atomic32 cur_state = base::subtle::NoBarrier_Load(&state_); |
128 | 36.2M | while (true) { |
129 | 36.2M | Atomic32 expected = cur_state & kNumReadersMask; // I expect some 0+ readers |
130 | 36.2M | Atomic32 try_new_state = kWriteFlag | expected; // I want to lock the other writers |
131 | | // Note: we use NoBarrier here because we'll do the Acquire barrier down below |
132 | | // in WaitPendingReaders |
133 | 36.2M | cur_state = base::subtle::NoBarrier_CompareAndSwap(&state_, expected, try_new_state); |
134 | 36.2M | if (cur_state == expected) |
135 | 31.4M | break; |
136 | | // Either was already locked by someone else, or CAS failed. |
137 | 4.83M | boost::detail::yield(loop_count++); |
138 | 4.83M | } |
139 | | |
140 | 31.4M | WaitPendingReaders(); |
141 | | |
142 | 31.4M | #ifndef NDEBUG |
143 | 31.4M | AssignWriterTid(); |
144 | 31.4M | #endif // NDEBUG |
145 | 31.4M | RecordLockHolderStack(); |
146 | 31.4M | } |
147 | | |
148 | 31.4M | void unlock() { |
149 | | // I expect to be the only writer |
150 | 31.4M | DCHECK_EQ(base::subtle::NoBarrier_Load(&state_), kWriteFlag); |
151 | | |
152 | 31.4M | #ifndef NDEBUG |
153 | 31.4M | writer_tid_ = kInvalidThreadId; // Invalid tid. |
154 | 31.4M | #endif // NDEBUG |
155 | | |
156 | 31.4M | ResetLockHolderStack(); |
157 | | // Reset: no writers & no readers. |
158 | 31.4M | Release_Store(&state_, 0); |
159 | 31.4M | } |
160 | | |
161 | | // Return true if the lock is currently held for write by any thread. |
162 | | // See simple_semaphore::is_locked() for details about where this is useful. |
163 | 0 | bool is_write_locked() const { |
164 | 0 | return base::subtle::NoBarrier_Load(&state_) & kWriteFlag; |
165 | 0 | } |
166 | | |
167 | | // Return true if the lock is currently held, either for read or write |
168 | | // by any thread. |
169 | | // See simple_semaphore::is_locked() for details about where this is useful. |
170 | 4 | bool is_locked() const { |
171 | 4 | return base::subtle::NoBarrier_Load(&state_); |
172 | 4 | } |
173 | | |
174 | | private: |
175 | | static const uint32_t kNumReadersMask = 0x7fffffff; |
176 | | static const uint32_t kWriteFlag = 1 << 31; |
177 | | |
178 | | #ifdef RW_SEMAPHORE_TRACK_HOLDER |
179 | | StackTrace writer_stack_; |
180 | | void RecordLockHolderStack() { |
181 | | writer_stack_.Collect(); |
182 | | } |
183 | | void ResetLockHolderStack() { |
184 | | writer_stack_.Reset(); |
185 | | } |
186 | | #else |
187 | 31.4M | void RecordLockHolderStack() { |
188 | 31.4M | } |
189 | 31.4M | void ResetLockHolderStack() { |
190 | 31.4M | } |
191 | | #endif |
192 | | |
193 | 31.4M | void WaitPendingReaders() { |
194 | 31.4M | int loop_count = 0; |
195 | 31.4M | while ((base::subtle::Acquire_Load(&state_) & kNumReadersMask) > 0) { |
196 | 36.4k | boost::detail::yield(loop_count++); |
197 | 36.4k | } |
198 | 31.4M | } |
199 | | |
200 | | private: |
201 | | volatile Atomic32 state_; |
202 | | #ifndef NDEBUG |
203 | | void AssignWriterTid(); |
204 | | |
205 | | int64_t writer_tid_ = kInvalidThreadId; |
206 | | #endif // NDEBUG |
207 | | }; |
208 | | |
209 | | } // namespace yb |
210 | | #endif /* YB_UTIL_RW_SEMAPHORE_H */ |