]> git.proxmox.com Git - ceph.git/blob - ceph/src/common/RWLock.h
import ceph quincy 17.2.4
[ceph.git] / ceph / src / common / RWLock.h
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3 /*
4 * Ceph - scalable distributed file system
5 *
6 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
7 *
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
12 *
13 */
14
15
16
17 #ifndef CEPH_RWLock_Posix__H
18 #define CEPH_RWLock_Posix__H
19
20 #include <pthread.h>
21 #include <string>
22 #include "include/ceph_assert.h"
23 #include "acconfig.h"
24 #include "lockdep.h"
25 #include "common/valgrind.h"
26
27 #include <atomic>
28
29 class RWLock final
30 {
31 mutable pthread_rwlock_t L;
32 std::string name;
33 mutable int id;
34 mutable std::atomic<unsigned> nrlock = { 0 }, nwlock = { 0 };
35 bool track, lockdep;
36
37 std::string unique_name(const char* name) const;
38
39 public:
40 RWLock(const RWLock& other) = delete;
41 const RWLock& operator=(const RWLock& other) = delete;
42
43 RWLock(const std::string &n, bool track_lock=true, bool ld=true, bool prioritize_write=false)
44 : name(n), id(-1), track(track_lock),
45 lockdep(ld) {
46 #if defined(HAVE_PTHREAD_RWLOCKATTR_SETKIND_NP)
47 if (prioritize_write) {
48 pthread_rwlockattr_t attr;
49 pthread_rwlockattr_init(&attr);
50 // PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP
51 // Setting the lock kind to this avoids writer starvation as long as
52 // long as any read locking is not done in a recursive fashion.
53 pthread_rwlockattr_setkind_np(&attr,
54 PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
55 pthread_rwlock_init(&L, &attr);
56 pthread_rwlockattr_destroy(&attr);
57 } else
58 #endif
59 // Next block is in {} to possibly connect to the above if when code is used.
60 {
61 pthread_rwlock_init(&L, NULL);
62 }
63 ANNOTATE_BENIGN_RACE_SIZED(&id, sizeof(id), "RWLock lockdep id");
64 ANNOTATE_BENIGN_RACE_SIZED(&nrlock, sizeof(nrlock), "RWlock nrlock");
65 ANNOTATE_BENIGN_RACE_SIZED(&nwlock, sizeof(nwlock), "RWlock nwlock");
66 if (lockdep && g_lockdep) id = lockdep_register(name.c_str());
67 }
68
69 bool is_locked() const {
70 ceph_assert(track);
71 return (nrlock > 0) || (nwlock > 0);
72 }
73
74 bool is_wlocked() const {
75 ceph_assert(track);
76 return (nwlock > 0);
77 }
78 ~RWLock() {
79 // The following check is racy but we are about to destroy
80 // the object and we assume that there are no other users.
81 if (track)
82 ceph_assert(!is_locked());
83 pthread_rwlock_destroy(&L);
84 if (lockdep && g_lockdep) {
85 lockdep_unregister(id);
86 }
87 }
88
89 void unlock(bool lockdep=true) const {
90 if (track) {
91 if (nwlock > 0) {
92 nwlock--;
93 } else {
94 ceph_assert(nrlock > 0);
95 nrlock--;
96 }
97 }
98 if (lockdep && this->lockdep && g_lockdep)
99 id = lockdep_will_unlock(name.c_str(), id);
100 int r = pthread_rwlock_unlock(&L);
101 ceph_assert(r == 0);
102 }
103
104 // read
105 void get_read() const {
106 if (lockdep && g_lockdep) id = lockdep_will_lock(name.c_str(), id);
107 int r = pthread_rwlock_rdlock(&L);
108 ceph_assert(r == 0);
109 if (lockdep && g_lockdep) id = lockdep_locked(name.c_str(), id);
110 if (track)
111 nrlock++;
112 }
113 bool try_get_read() const {
114 if (pthread_rwlock_tryrdlock(&L) == 0) {
115 if (track)
116 nrlock++;
117 if (lockdep && g_lockdep) id = lockdep_locked(name.c_str(), id);
118 return true;
119 }
120 return false;
121 }
122 void put_read() const {
123 unlock();
124 }
125 void lock_shared() {
126 get_read();
127 }
128 void unlock_shared() {
129 put_read();
130 }
131 // write
132 void get_write(bool lockdep=true) {
133 if (lockdep && this->lockdep && g_lockdep)
134 id = lockdep_will_lock(name.c_str(), id);
135 int r = pthread_rwlock_wrlock(&L);
136 ceph_assert(r == 0);
137 if (lockdep && this->lockdep && g_lockdep)
138 id = lockdep_locked(name.c_str(), id);
139 if (track)
140 nwlock++;
141
142 }
143 bool try_get_write(bool lockdep=true) {
144 if (pthread_rwlock_trywrlock(&L) == 0) {
145 if (lockdep && this->lockdep && g_lockdep)
146 id = lockdep_locked(name.c_str(), id);
147 if (track)
148 nwlock++;
149 return true;
150 }
151 return false;
152 }
153 void put_write() {
154 unlock();
155 }
156 void lock() {
157 get_write();
158 }
159 void get(bool for_write) {
160 if (for_write) {
161 get_write();
162 } else {
163 get_read();
164 }
165 }
166
167 public:
168 class RLocker {
169 const RWLock &m_lock;
170
171 bool locked;
172
173 public:
174 explicit RLocker(const RWLock& lock) : m_lock(lock) {
175 m_lock.get_read();
176 locked = true;
177 }
178 void unlock() {
179 ceph_assert(locked);
180 m_lock.unlock();
181 locked = false;
182 }
183 ~RLocker() {
184 if (locked) {
185 m_lock.unlock();
186 }
187 }
188 };
189
190 class WLocker {
191 RWLock &m_lock;
192
193 bool locked;
194
195 public:
196 explicit WLocker(RWLock& lock) : m_lock(lock) {
197 m_lock.get_write();
198 locked = true;
199 }
200 void unlock() {
201 ceph_assert(locked);
202 m_lock.unlock();
203 locked = false;
204 }
205 ~WLocker() {
206 if (locked) {
207 m_lock.unlock();
208 }
209 }
210 };
211
212 class Context {
213 RWLock& lock;
214
215 public:
216 enum LockState {
217 Untaken = 0,
218 TakenForRead = 1,
219 TakenForWrite = 2,
220 };
221
222 private:
223 LockState state;
224
225 public:
226 explicit Context(RWLock& l) : lock(l), state(Untaken) {}
227 Context(RWLock& l, LockState s) : lock(l), state(s) {}
228
229 void get_write() {
230 ceph_assert(state == Untaken);
231
232 lock.get_write();
233 state = TakenForWrite;
234 }
235
236 void get_read() {
237 ceph_assert(state == Untaken);
238
239 lock.get_read();
240 state = TakenForRead;
241 }
242
243 void unlock() {
244 ceph_assert(state != Untaken);
245 lock.unlock();
246 state = Untaken;
247 }
248
249 void promote() {
250 ceph_assert(state == TakenForRead);
251 unlock();
252 get_write();
253 }
254
255 LockState get_state() { return state; }
256 void set_state(LockState s) {
257 state = s;
258 }
259
260 bool is_locked() {
261 return (state != Untaken);
262 }
263
264 bool is_rlocked() {
265 return (state == TakenForRead);
266 }
267
268 bool is_wlocked() {
269 return (state == TakenForWrite);
270 }
271 };
272 };
273
274 #endif // !CEPH_RWLock_Posix__H