Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / 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/assert.h>
23 #include "lockdep.h"
24 #include "common/valgrind.h"
25
26 #include <atomic>
27
28 class RWLock final
29 {
30   mutable pthread_rwlock_t L;
31   std::string name;
32   mutable int id;
33   mutable std::atomic<unsigned> nrlock = { 0 }, nwlock = { 0 };
34   bool track, lockdep;
35
36   std::string unique_name(const char* name) const;
37
38 public:
39   RWLock(const RWLock& other) = delete;
40   const RWLock& operator=(const RWLock& other) = delete;
41
42   RWLock(const std::string &n, bool track_lock=true, bool ld=true, bool prioritize_write=false)
43     : name(n), id(-1), track(track_lock),
44       lockdep(ld) {
45 #if defined(PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP)
46     if (prioritize_write) {
47       pthread_rwlockattr_t attr;
48       pthread_rwlockattr_init(&attr);
49       // PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP
50       //   Setting the lock kind to this avoids writer starvation as long as
51       //   long as any read locking is not done in a recursive fashion.
52       pthread_rwlockattr_setkind_np(&attr,
53           PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
54       pthread_rwlock_init(&L, &attr);
55     } else 
56 #endif 
57     // Next block is in {} to possibly connect to the above if when code is used.
58     {
59       pthread_rwlock_init(&L, NULL);
60     }
61     ANNOTATE_BENIGN_RACE_SIZED(&id, sizeof(id), "RWLock lockdep id");
62     ANNOTATE_BENIGN_RACE_SIZED(&nrlock, sizeof(nrlock), "RWlock nrlock");
63     ANNOTATE_BENIGN_RACE_SIZED(&nwlock, sizeof(nwlock), "RWlock nwlock");
64     if (lockdep && g_lockdep) id = lockdep_register(name.c_str());
65   }
66
67   bool is_locked() const {
68     assert(track);
69     return (nrlock > 0) || (nwlock > 0);
70   }
71
72   bool is_wlocked() const {
73     assert(track);
74     return (nwlock > 0);
75   }
76   ~RWLock() {
77     // The following check is racy but we are about to destroy
78     // the object and we assume that there are no other users.
79     if (track)
80       assert(!is_locked());
81     pthread_rwlock_destroy(&L);
82     if (lockdep && g_lockdep) {
83       lockdep_unregister(id);
84     }
85   }
86
87   void unlock(bool lockdep=true) const {
88     if (track) {
89       if (nwlock > 0) {
90         nwlock--;
91       } else {
92         assert(nrlock > 0);
93         nrlock--;
94       }
95     }
96     if (lockdep && this->lockdep && g_lockdep)
97       id = lockdep_will_unlock(name.c_str(), id);
98     int r = pthread_rwlock_unlock(&L);
99     assert(r == 0);
100   }
101
102   // read
103   void get_read() const {
104     if (lockdep && g_lockdep) id = lockdep_will_lock(name.c_str(), id);
105     int r = pthread_rwlock_rdlock(&L);
106     assert(r == 0);
107     if (lockdep && g_lockdep) id = lockdep_locked(name.c_str(), id);
108     if (track)
109       nrlock++;
110   }
111   bool try_get_read() const {
112     if (pthread_rwlock_tryrdlock(&L) == 0) {
113       if (track)
114          nrlock++;
115       if (lockdep && g_lockdep) id = lockdep_locked(name.c_str(), id);
116       return true;
117     }
118     return false;
119   }
120   void put_read() const {
121     unlock();
122   }
123
124   // write
125   void get_write(bool lockdep=true) {
126     if (lockdep && this->lockdep && g_lockdep)
127       id = lockdep_will_lock(name.c_str(), id);
128     int r = pthread_rwlock_wrlock(&L);
129     assert(r == 0);
130     if (lockdep && this->lockdep && g_lockdep)
131       id = lockdep_locked(name.c_str(), id);
132     if (track)
133       nwlock++;
134
135   }
136   bool try_get_write(bool lockdep=true) {
137     if (pthread_rwlock_trywrlock(&L) == 0) {
138       if (lockdep && this->lockdep && g_lockdep)
139         id = lockdep_locked(name.c_str(), id);
140       if (track)
141          nwlock++;
142       return true;
143     }
144     return false;
145   }
146   void put_write() {
147     unlock();
148   }
149
150   void get(bool for_write) {
151     if (for_write) {
152       get_write();
153     } else {
154       get_read();
155     }
156   }
157
158 public:
159   class RLocker {
160     const RWLock &m_lock;
161
162     bool locked;
163
164   public:
165    explicit  RLocker(const RWLock& lock) : m_lock(lock) {
166       m_lock.get_read();
167       locked = true;
168     }
169     void unlock() {
170       assert(locked);
171       m_lock.unlock();
172       locked = false;
173     }
174     ~RLocker() {
175       if (locked) {
176         m_lock.unlock();
177       }
178     }
179   };
180
181   class WLocker {
182     RWLock &m_lock;
183
184     bool locked;
185
186   public:
187     explicit WLocker(RWLock& lock) : m_lock(lock) {
188       m_lock.get_write();
189       locked = true;
190     }
191     void unlock() {
192       assert(locked);
193       m_lock.unlock();
194       locked = false;
195     }
196     ~WLocker() {
197       if (locked) {
198         m_lock.unlock();
199       }
200     }
201   };
202
203   class Context {
204     RWLock& lock;
205
206   public:
207     enum LockState {
208       Untaken = 0,
209       TakenForRead = 1,
210       TakenForWrite = 2,
211     };
212
213   private:
214     LockState state;
215
216   public:
217     explicit Context(RWLock& l) : lock(l), state(Untaken) {}
218     Context(RWLock& l, LockState s) : lock(l), state(s) {}
219
220     void get_write() {
221       assert(state == Untaken);
222
223       lock.get_write();
224       state = TakenForWrite;
225     }
226
227     void get_read() {
228       assert(state == Untaken);
229
230       lock.get_read();
231       state = TakenForRead;
232     }
233
234     void unlock() {
235       assert(state != Untaken);
236       lock.unlock();
237       state = Untaken;
238     }
239
240     void promote() {
241       assert(state == TakenForRead);
242       unlock();
243       get_write();
244     }
245
246     LockState get_state() { return state; }
247     void set_state(LockState s) {
248       state = s;
249     }
250
251     bool is_locked() {
252       return (state != Untaken);
253     }
254
255     bool is_rlocked() {
256       return (state == TakenForRead);
257     }
258
259     bool is_wlocked() {
260       return (state == TakenForWrite);
261     }
262   };
263 };
264
265 #endif // !CEPH_RWLock_Posix__H