YugabyteDB (2.13.1.0-b60, 21121d69985fbf76aa6958d8f04a9bfa936293b5)

Coverage Report

Created: 2022-03-22 16:43

/Users/deen/code/yugabyte-db/src/yb/util/rwc_lock.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_RWC_LOCK_H
33
#define YB_UTIL_RWC_LOCK_H
34
35
#ifndef NDEBUG
36
#include <unordered_map>
37
#endif // NDEBUG
38
39
#include "yb/gutil/macros.h"
40
41
#include "yb/util/condition_variable.h"
42
#include "yb/util/mutex.h"
43
#include "yb/util/stack_trace.h"
44
45
namespace yb {
46
47
// A read-write-commit lock.
48
//
49
// This lock has three modes: read, write, and commit.
50
// The lock compatibility matrix is as follows:
51
//
52
//           Read    Write    Commit
53
//  Read      X        X
54
//  Write     X
55
//  Commit
56
//
57
// An 'X' indicates that the two types of locks may be
58
// held at the same time.
59
//
60
// In prose:
61
// - Multiple threads may hold the Read lock at the same time.
62
// - A single thread may hold the Write lock, potentially at the
63
//   same time as any number of readers.
64
// - A single thread may hold the Commit lock, but this lock is completely
65
//   exclusive (no concurrent readers or writers).
66
//
67
// A typical use case for this type of lock is when a structure is read often,
68
// occasionally updated, and the update operation can take a long time. In this
69
// use case, the readers simply use ReadLock() and ReadUnlock(), while the
70
// writer uses a copy-on-write technique like:
71
//
72
//   obj->lock.WriteLock();
73
//   // NOTE: cannot safely mutate obj->state directly here, since readers
74
//   // may be concurrent! So, we make a local copy to mutate.
75
//   my_local_copy = obj->state;
76
//   SomeLengthyMutation(my_local_copy);
77
//   obj->lock.UpgradeToCommitLock();
78
//   obj->state = my_local_copy;
79
//   obj->lock.CommitUnlock();
80
//
81
// This is more efficient than a standard Reader-Writer lock since the lengthy
82
// mutation is only protected against other concurrent mutators, and readers
83
// may continue to run with no contention.
84
//
85
// For the common pattern described above, the 'CowObject<>' template class defined
86
// in cow_object.h is more convenient than manual locking.
87
//
88
// NOTE: this implementation currently does not implement any starvation protection
89
// or fairness. If the read lock is being constantly acquired (i.e reader count
90
// never drops to 0) then UpgradeToCommitLock() may block arbitrarily long.
91
class RWCLock {
92
 public:
93
  RWCLock();
94
  ~RWCLock();
95
96
  // Acquire the lock in read mode. Upon return, guarantees that:
97
  // - Other threads may concurrently hold the lock for Read.
98
  // - Either zero or one thread may hold the lock for Write.
99
  // - No threads hold the lock for Commit.
100
  void ReadLock();
101
  void ReadUnlock();
102
103
  // Return true if there are any readers currently holding the lock.
104
  // Useful for debug assertions.
105
  bool HasReaders() const;
106
107
  // Return true if the current thread holds the write lock.
108
  //
109
  // In DEBUG mode this is accurate -- we track the current holder's tid.
110
  // In non-DEBUG mode, this may sometimes return true even if another thread
111
  // is in fact the holder.
112
  // Thus, this is only really useful in the context of a DCHECK assertion.
113
  bool HasWriteLock() const;
114
115
  // Boost-like wrappers, so boost lock guards work
116
0
  void lock_shared() { ReadLock(); }
117
0
  void unlock_shared() { ReadUnlock(); }
118
119
  // Acquire the lock in write mode. Upon return, guarantees that:
120
  // - Other threads may concurrently hold the lock for Read.
121
  // - No other threads hold the lock for Write or Commit.
122
  void WriteLock();
123
  void WriteUnlock();
124
125
  // Boost-like wrappers
126
0
  void lock() { WriteLock(); }
127
0
  void unlock() { WriteUnlock(); }
128
129
  // Upgrade the lock from Write mode to Commit mode.
130
  // Requires that the current thread holds the lock in Write mode.
131
  // Upon return, guarantees:
132
  // - No other thread holds the lock in any mode.
133
  void UpgradeToCommitLock();
134
  void CommitUnlock();
135
136
 private:
137
  // Lock which protects reader_count_ and write_locked_.
138
  // Additionally, while the commit lock is held, the
139
  // locking thread holds this mutex, which prevents any new
140
  // threads from obtaining the lock in any mode.
141
  mutable Mutex lock_;
142
  ConditionVariable no_mutators_, no_readers_;
143
  int reader_count_;
144
  bool write_locked_;
145
146
#ifndef NDEBUG
147
  int64_t last_writer_tid_;
148
  int64_t last_writelock_acquire_time_;
149
  StackTrace last_writer_stacktrace_;
150
151
  // thread id --> (thread's reader count, stack trace of first reader).
152
  struct CountStack {
153
    int count; // reader count
154
    StackTrace stack; // stack trace of first reader
155
  };
156
  std::unordered_map<int64_t, CountStack> reader_stacks_;
157
#endif // NDEBUG
158
159
  DISALLOW_COPY_AND_ASSIGN(RWCLock);
160
};
161
162
} // namespace yb
163
#endif /* YB_UTIL_RWC_LOCK_H */