/Users/deen/code/yugabyte-db/src/yb/util/rwc_lock.cc
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 | | |
33 | | #include "yb/util/rwc_lock.h" |
34 | | |
35 | | #include <glog/logging.h> |
36 | | |
37 | | #ifndef NDEBUG |
38 | | #include <utility> |
39 | | #endif // NDEBUG |
40 | | |
41 | | #ifndef NDEBUG |
42 | | #include "yb/gutil/walltime.h" |
43 | | #include "yb/util/debug-util.h" |
44 | | #include "yb/util/env.h" |
45 | | #include "yb/util/thread.h" |
46 | | #endif // NDEBUG |
47 | | |
48 | | namespace yb { |
49 | | |
50 | | namespace { |
51 | | |
52 | | const auto kFirstWait = MonoDelta::FromSeconds(1); |
53 | | const auto kSecondWait = MonoDelta::FromSeconds(180); |
54 | | |
55 | | } // namespace |
56 | | |
57 | | RWCLock::RWCLock() |
58 | | : no_mutators_(&lock_), |
59 | | no_readers_(&lock_), |
60 | | reader_count_(0), |
61 | | #ifdef NDEBUG |
62 | | write_locked_(false) { |
63 | | #else |
64 | | write_locked_(false), |
65 | | last_writer_tid_(0), |
66 | 329k | last_writelock_acquire_time_(0) { |
67 | 329k | #endif // NDEBUG |
68 | 329k | } |
69 | | |
70 | 8.09k | RWCLock::~RWCLock() { |
71 | 8.09k | CHECK_EQ(reader_count_, 0); |
72 | 8.09k | } |
73 | | |
74 | 144M | void RWCLock::ReadLock() { |
75 | 144M | MutexLock l(lock_); |
76 | 144M | reader_count_++; |
77 | 144M | #ifndef NDEBUG |
78 | 0 | if (VLOG_IS_ON(1)) { |
79 | 0 | const int64_t tid = Thread::CurrentThreadId(); |
80 | 0 | if (reader_stacks_.find(tid) == reader_stacks_.end()) { |
81 | 0 | StackTrace stack_trace = StackTrace(); |
82 | 0 | stack_trace.Collect(); |
83 | 0 | reader_stacks_[tid] = { |
84 | 0 | .count = 1, |
85 | 0 | .stack = std::move(stack_trace), |
86 | 0 | }; |
87 | 0 | } else { |
88 | 0 | reader_stacks_[tid].count++; |
89 | 0 | } |
90 | 0 | } |
91 | 144M | #endif // NDEBUG |
92 | 144M | } |
93 | | |
94 | 144M | void RWCLock::ReadUnlock() { |
95 | 144M | MutexLock l(lock_); |
96 | 144M | DCHECK_GT(reader_count_, 0); |
97 | 144M | reader_count_--; |
98 | 144M | #ifndef NDEBUG |
99 | 0 | if (VLOG_IS_ON(1)) { |
100 | 0 | const int64_t tid = Thread::CurrentThreadId(); |
101 | 0 | if (--reader_stacks_[tid].count == 0) { |
102 | 0 | reader_stacks_.erase(tid); |
103 | 0 | } |
104 | 0 | } |
105 | 144M | #endif // NDEBUG |
106 | 144M | if (reader_count_ == 0) { |
107 | 129M | no_readers_.Signal(); |
108 | 129M | } |
109 | 144M | } |
110 | | |
111 | 118M | bool RWCLock::HasReaders() const { |
112 | 118M | MutexLock l(lock_); |
113 | 118M | return reader_count_ > 0; |
114 | 118M | } |
115 | | |
116 | 1.28M | bool RWCLock::HasWriteLock() const { |
117 | 1.28M | MutexLock l(lock_); |
118 | 1.28M | #ifndef NDEBUG |
119 | 1.28M | return last_writer_tid_ == Thread::CurrentThreadId(); |
120 | | #else |
121 | | return write_locked_; |
122 | | #endif |
123 | 1.28M | } |
124 | | |
125 | 1.18M | void RWCLock::WriteLock() { |
126 | 1.18M | MutexLock l(lock_); |
127 | | // Wait for any other mutations to finish. |
128 | 1.18M | #ifndef NDEBUG |
129 | 1.18M | bool first_wait = true; |
130 | 1.25M | while (write_locked_) { |
131 | 76.2k | if (!no_mutators_.TimedWait(first_wait ? kFirstWait : kSecondWait)) { |
132 | 66 | std::ostringstream ss; |
133 | 66 | ss << "Too long write lock wait, last writer stack: " << last_writer_stacktrace_.Symbolize(); |
134 | 66 | if (VLOG_IS_ON(1) || !first_wait) { |
135 | 7 | ss << "current thread stack: " << GetStackTrace(); |
136 | 7 | } |
137 | 66 | (first_wait ? LOG(WARNING) : LOG(FATAL)) << ss.str(); |
138 | 66 | } |
139 | 76.2k | first_wait = false; |
140 | 76.2k | } |
141 | | #else |
142 | | while (write_locked_) { |
143 | | no_mutators_.Wait(); |
144 | | } |
145 | | #endif |
146 | 1.18M | #ifndef NDEBUG |
147 | 1.18M | last_writelock_acquire_time_ = GetCurrentTimeMicros(); |
148 | 1.18M | last_writer_tid_ = Thread::CurrentThreadId(); |
149 | 1.18M | last_writer_stacktrace_.Collect(); |
150 | 1.18M | #endif // NDEBUG |
151 | 1.18M | write_locked_ = true; |
152 | 1.18M | } |
153 | | |
154 | 468k | void RWCLock::WriteUnlock() { |
155 | 468k | MutexLock l(lock_); |
156 | 468k | DCHECK(write_locked_); |
157 | 468k | write_locked_ = false; |
158 | 468k | #ifndef NDEBUG |
159 | 468k | last_writer_stacktrace_.Reset(); |
160 | 468k | #endif // NDEBUG |
161 | 468k | no_mutators_.Signal(); |
162 | 468k | } |
163 | | |
164 | 715k | void RWCLock::UpgradeToCommitLock() { |
165 | 715k | lock_.lock(); |
166 | 715k | DCHECK(write_locked_); |
167 | 715k | #ifndef NDEBUG |
168 | 715k | bool first_wait = true; |
169 | 716k | while (reader_count_ > 0) { |
170 | 330 | if (!no_readers_.TimedWait(first_wait ? kFirstWait : kSecondWait)) { |
171 | 0 | std::ostringstream ss; |
172 | 0 | ss << "Too long commit lock wait, num readers: " << reader_count_ |
173 | 0 | << ", current thread stack: " << GetStackTrace(); |
174 | 0 | if (VLOG_IS_ON(1)) { |
175 | 0 | for (const auto& entry : reader_stacks_) { |
176 | 0 | ss << "reader thread " << entry.first; |
177 | 0 | if (entry.second.count > 1) { |
178 | 0 | ss << " (holding " << entry.second.count << " locks) first"; |
179 | 0 | } |
180 | 0 | ss << " stack: " << entry.second.stack.Symbolize(); |
181 | 0 | } |
182 | 0 | } |
183 | 0 | (first_wait ? LOG(WARNING) : LOG(FATAL)) << ss.str(); |
184 | 0 | } |
185 | 330 | first_wait = false; |
186 | 330 | } |
187 | | #else |
188 | | while (reader_count_ > 0) { |
189 | | no_readers_.Wait(); |
190 | | } |
191 | | #endif |
192 | 715k | DCHECK(write_locked_); |
193 | | |
194 | | // Leaves the lock held, which prevents any new readers |
195 | | // or writers. |
196 | 715k | } |
197 | | |
198 | 715k | void RWCLock::CommitUnlock() { |
199 | 715k | DCHECK_EQ(0, reader_count_); |
200 | 715k | write_locked_ = false; |
201 | 715k | #ifndef NDEBUG |
202 | 715k | last_writer_stacktrace_.Reset(); |
203 | 715k | #endif // NDEBUG |
204 | 715k | no_mutators_.Broadcast(); |
205 | 715k | lock_.unlock(); |
206 | 715k | } |
207 | | |
208 | | } // namespace yb |