Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / common / shunique_lock.h
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3
4 #ifndef CEPH_COMMON_SHUNIQUE_LOCK_H
5 #define CEPH_COMMON_SHUNIQUE_LOCK_H
6
7 #include <mutex>
8 #include <system_error>
9 #include <boost/thread/shared_mutex.hpp>
10
11 namespace ceph {
12 // This is a 'lock' class in the style of shared_lock and
13 // unique_lock. Like shared_mutex it implements both Lockable and
14 // SharedLockable.
15
16 // My rationale is thus: one of the advantages of unique_lock is that
17 // I can pass a thread of execution's control of a lock around as a
18 // parameter. So that methods further down the call stack can unlock
19 // it, do something, relock it, and have the lock state be known by
20 // the caller afterward, explicitly. The shared_lock class offers a
21 // similar advantage to shared_lock, but each class is one or the
22 // other. In Objecter we have calls that in most cases need /a/ lock
23 // on the shared mutex, and whether it's shared or exclusive doesn't
24 // matter. In some circumstances they may drop the shared lock and
25 // reacquire an exclusive one. This could be handled by passing both a
26 // shared and unique lock down the call stack. This is vexacious and
27 // shameful.
28
29 // Wanting to avoid heaping shame and vexation upon myself, I threw
30 // this class together.
31
32 // This class makes no attempt to support atomic upgrade or
33 // downgrade. I don't want either. Matt has convinced me that if you
34 // think you want them you've usually made a mistake somewhere. It is
35 // exactly and only a reification of the state held on a shared mutex.
36
37 /// Acquire unique ownership of the mutex.
38 struct acquire_unique_t { };
39
40 /// Acquire shared ownership of the mutex.
41 struct acquire_shared_t { };
42
43 constexpr acquire_unique_t acquire_unique { };
44 constexpr acquire_shared_t acquire_shared { };
45
46 template<typename Mutex>
47 class shunique_lock {
48 public:
49   typedef Mutex mutex_type;
50   typedef std::unique_lock<Mutex> unique_lock_type;
51   typedef boost::shared_lock<Mutex> shared_lock_type;
52
53   shunique_lock() noexcept : m(nullptr), o(ownership::none) { }
54
55   // We do not provide a default locking/try_locking constructor that
56   // takes only the mutex, since it is not clear whether to take it
57   // shared or unique. We explicitly require the use of lock_deferred
58   // to prevent Nasty Surprises.
59
60   shunique_lock(mutex_type& m, std::defer_lock_t) noexcept
61     : m(&m), o(ownership::none) { }
62
63   shunique_lock(mutex_type& m, acquire_unique_t)
64     : m(&m), o(ownership::none) {
65     lock();
66   }
67
68   shunique_lock(mutex_type& m, acquire_shared_t)
69     : m(&m), o(ownership::none) {
70     lock_shared();
71   }
72
73   template<typename AcquireType>
74   shunique_lock(mutex_type& m, AcquireType at, std::try_to_lock_t)
75     : m(&m), o(ownership::none) {
76     try_lock(at);
77   }
78
79   shunique_lock(mutex_type& m, acquire_unique_t, std::adopt_lock_t)
80     : m(&m), o(ownership::unique) {
81     // You'd better actually have a lock, or I will find you and I
82     // will hunt you down.
83   }
84
85   shunique_lock(mutex_type& m, acquire_shared_t, std::adopt_lock_t)
86     : m(&m), o(ownership::shared) {
87   }
88
89   template<typename AcquireType, typename Clock, typename Duration>
90   shunique_lock(mutex_type& m, AcquireType at,
91                 const std::chrono::time_point<Clock, Duration>& t)
92     : m(&m), o(ownership::none) {
93     try_lock_until(at, t);
94   }
95
96   template<typename AcquireType, typename Rep, typename Period>
97   shunique_lock(mutex_type& m, AcquireType at,
98                 const std::chrono::duration<Rep, Period>& dur)
99     : m(&m), o(ownership::none) {
100     try_lock_for(at, dur);
101   }
102
103   ~shunique_lock() {
104     switch (o) {
105     case ownership::none:
106       return;
107       break;
108     case ownership::unique:
109       m->unlock();
110       break;
111     case ownership::shared:
112       m->unlock_shared();
113       break;
114     }
115   }
116
117   shunique_lock(shunique_lock const&) = delete;
118   shunique_lock& operator=(shunique_lock const&) = delete;
119
120   shunique_lock(shunique_lock&& l) noexcept : shunique_lock() {
121     swap(l);
122   }
123
124   shunique_lock(unique_lock_type&& l) noexcept {
125     if (l.owns_lock())
126       o = ownership::unique;
127     else
128       o = ownership::none;
129     m = l.release();
130   }
131
132   shunique_lock(shared_lock_type&& l) noexcept {
133     if (l.owns_lock())
134       o = ownership::shared;
135     else
136       o = ownership::none;
137     m = l.release();
138   }
139
140   shunique_lock& operator=(shunique_lock&& l) noexcept {
141     shunique_lock(std::move(l)).swap(*this);
142     return *this;
143   }
144
145   shunique_lock& operator=(unique_lock_type&& l) noexcept {
146     shunique_lock(std::move(l)).swap(*this);
147     return *this;
148   }
149
150   shunique_lock& operator=(shared_lock_type&& l) noexcept {
151     shunique_lock(std::move(l)).swap(*this);
152     return *this;
153   }
154
155   void lock() {
156     lockable();
157     m->lock();
158     o = ownership::unique;
159   }
160
161   void lock_shared() {
162     lockable();
163     m->lock_shared();
164     o = ownership::shared;
165   }
166
167   void lock(ceph::acquire_unique_t) {
168     lock();
169   }
170
171   void lock(ceph::acquire_shared_t) {
172     lock_shared();
173   }
174
175   bool try_lock() {
176     lockable();
177     if (m->try_lock()) {
178       o = ownership::unique;
179       return true;
180     }
181     return false;
182   }
183
184   bool try_lock_shared() {
185     lockable();
186     if (m->try_lock_shared()) {
187       o = ownership::shared;
188       return true;
189     }
190     return false;
191   }
192
193   bool try_lock(ceph::acquire_unique_t) {
194     return try_lock();
195   }
196
197   bool try_lock(ceph::acquire_shared_t) {
198     return try_lock_shared();
199   }
200
201   template<typename Rep, typename Period>
202   bool try_lock_for(const std::chrono::duration<Rep, Period>& dur) {
203     lockable();
204     if (m->try_lock_for(dur)) {
205       o = ownership::unique;
206       return true;
207     }
208     return false;
209   }
210
211   template<typename Rep, typename Period>
212   bool try_lock_shared_for(const std::chrono::duration<Rep, Period>& dur) {
213     lockable();
214     if (m->try_lock_shared_for(dur)) {
215       o = ownership::shared;
216       return true;
217     }
218     return false;
219   }
220
221   template<typename Rep, typename Period>
222   bool try_lock_for(ceph::acquire_unique_t,
223                     const std::chrono::duration<Rep, Period>& dur) {
224     return try_lock_for(dur);
225   }
226
227   template<typename Rep, typename Period>
228   bool try_lock_for(ceph::acquire_shared_t,
229                     const std::chrono::duration<Rep, Period>& dur) {
230     return try_lock_shared_for(dur);
231   }
232
233   template<typename Clock, typename Duration>
234   bool try_lock_until(const std::chrono::time_point<Clock, Duration>& time) {
235     lockable();
236     if (m->try_lock_until(time)) {
237       o = ownership::unique;
238       return true;
239     }
240     return false;
241   }
242
243   template<typename Clock, typename Duration>
244   bool try_lock_shared_until(const std::chrono::time_point<Clock,
245                              Duration>& time) {
246     lockable();
247     if (m->try_lock_shared_until(time)) {
248       o = ownership::shared;
249       return true;
250     }
251     return false;
252   }
253
254   template<typename Clock, typename Duration>
255   bool try_lock_until(ceph::acquire_unique_t,
256                       const std::chrono::time_point<Clock, Duration>& time) {
257     return try_lock_until(time);
258   }
259
260   template<typename Clock, typename Duration>
261   bool try_lock_until(ceph::acquire_shared_t,
262                       const std::chrono::time_point<Clock, Duration>& time) {
263     return try_lock_shared_until(time);
264   }
265
266   // Only have a single unlock method. Otherwise we'd be building an
267   // Acme lock class suitable only for ravenous coyotes desparate to
268   // devour a road runner. It would be bad. It would be disgusting. It
269   // would be infelicitous as heck. It would leave our developers in a
270   // state of seeming safety unaware of the yawning chasm of failure
271   // that had opened beneath their feet that would soon transition
272   // into a sickening realization of the error they made and a brief
273   // moment of blinking self pity before their program hurled itself
274   // into undefined behaviour and plummeted up the stack with core
275   // dumps trailing behind it.
276
277   void unlock() {
278     switch (o) {
279     case ownership::none:
280       throw std::system_error((int)std::errc::resource_deadlock_would_occur,
281                               std::generic_category());
282       break;
283
284     case ownership::unique:
285       m->unlock();
286       break;
287
288     case ownership::shared:
289       m->unlock_shared();
290       break;
291     }
292     o = ownership::none;
293   }
294
295   // Setters
296
297   void swap(shunique_lock& u) noexcept {
298     std::swap(m, u.m);
299     std::swap(o, u.o);
300   }
301
302   mutex_type* release() noexcept {
303     o = ownership::none;
304     mutex_type* tm = m;
305     m = nullptr;
306     return tm;
307   }
308
309   // Ideally I'd rather make a move constructor for std::unique_lock
310   // that took a shunique_lock, but obviously I can't.
311   unique_lock_type release_to_unique() {
312     if (o == ownership::unique) {
313       o = ownership::none;
314       unique_lock_type tu(*m, std::adopt_lock);
315       m = nullptr;
316       return tu;
317     } else if (o == ownership::none) {
318       unique_lock_type tu(*m, std::defer_lock);
319       m = nullptr;
320       return tu;
321     } else if (m == nullptr) {
322       return unique_lock_type();
323     }
324     throw std::system_error((int)std::errc::operation_not_permitted,
325                             std::generic_category());
326     return unique_lock_type();
327   }
328
329   shared_lock_type release_to_shared() {
330     if (o == ownership::shared) {
331       o = ownership::none;
332       shared_lock_type ts(*m, std::adopt_lock);
333       m = nullptr;
334       return ts;
335     } else if (o == ownership::none) {
336       shared_lock_type ts(*m, std::defer_lock);
337       m = nullptr;
338       return ts;
339     } else if (m == nullptr) {
340       return shared_lock_type();
341     }
342     throw std::system_error((int)std::errc::operation_not_permitted,
343                             std::generic_category());
344     return shared_lock_type();
345   }
346
347   // Getters
348
349   // Note that this returns true if the lock UNIQUE, it will return
350   // false for shared
351   bool owns_lock() const noexcept {
352     return o == ownership::unique;
353   }
354
355   bool owns_lock_shared() const noexcept {
356     return o == ownership::shared;
357   }
358
359   // If you want to make sure you have a lock of some sort on the
360   // mutex, just treat as a bool.
361   explicit operator bool() const noexcept {
362     return o != ownership::none;
363   }
364
365   mutex_type* mutex() const noexcept {
366     return m;
367   }
368
369 private:
370   void lockable() const {
371     if (m == nullptr)
372       throw std::system_error((int)std::errc::operation_not_permitted,
373                               std::generic_category());
374     if (o != ownership::none)
375       throw std::system_error((int)std::errc::resource_deadlock_would_occur,
376                               std::generic_category());
377   }
378
379   mutex_type*   m;
380   enum struct ownership : uint8_t {
381     none, unique, shared
382       };
383   ownership o;
384 };
385 } // namespace ceph
386
387 namespace std {
388   template<typename Mutex>
389   void swap(ceph::shunique_lock<Mutex> sh1,
390             ceph::shunique_lock<Mutex> sha) {
391     sh1.swap(sha);
392   }
393 } // namespace std
394
395 #endif // CEPH_COMMON_SHUNIQUE_LOCK_H