YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/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