/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 */ |