1 // +------------------------------------------------------------------+
2 // | ____ _ _ __ __ _ __ |
3 // | / ___| |__ ___ ___| | __ | \/ | |/ / |
4 // | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
5 // | | |___| | | | __/ (__| < | | | | . \ |
6 // | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
7 // | |
8 // | Copyright Mathias Kettner 2014 mk@mathias-kettner.de |
9 // +------------------------------------------------------------------+
10 //
11 // This file is part of Check_MK.
12 // The official homepage is at http://mathias-kettner.de/check_mk.
13 //
14 // check_mk is free software; you can redistribute it and/or modify it
15 // under the terms of the GNU General Public License as published by
16 // the Free Software Foundation in version 2. check_mk is distributed
17 // in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
18 // out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
19 // PARTICULAR PURPOSE. See the GNU General Public License for more de-
20 // ails. You should have received a copy of the GNU General Public
21 // License along with GNU Make; see the file COPYING. If not, write
22 // to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
23 // Boston, MA 02110-1301 USA.
24
25 #ifndef mk_SharedMutex_h
26 #define mk_SharedMutex_h
27
28 #include "config.h" // IWYU pragma: keep
29 #include <pthread.h>
30 #include "Mutex.h"
31
32 // A more or less drop-in replacement for C++14's <shared_mutex> (partial)
33
34 namespace mk {
35
36 // Note: This is not in the standard, but close enough to shared_mutex for our purposes.
37 class rw_mutex {
38 public:
39 typedef pthread_rwlock_t *native_handle_type;
rw_mutex()40 rw_mutex() { check_status(pthread_rwlock_init(&_mutex, 0)); }
~rw_mutex()41 ~rw_mutex() { check_status(pthread_rwlock_destroy(&_mutex)); }
lock()42 void lock() { check_status(pthread_rwlock_wrlock(&_mutex)); }
try_lock()43 bool try_lock()
44 {
45 int status = pthread_rwlock_trywrlock(&_mutex);
46 if (status != EBUSY) check_status(status);
47 return status == 0;
48 }
unlock()49 void unlock() { check_status(pthread_rwlock_unlock(&_mutex)); }
lock_shared()50 void lock_shared() { check_status(pthread_rwlock_rdlock(&_mutex)); }
try_lock_shared()51 bool try_lock_shared()
52 {
53 int status = pthread_rwlock_tryrdlock(&_mutex);
54 if (status != EBUSY) check_status(status);
55 return status == 0;
56 }
unlock_shared()57 void unlock_shared() { check_status(pthread_rwlock_unlock(&_mutex)); }
native_handle()58 native_handle_type native_handle() { return &_mutex; }
59 private:
60 rw_mutex(const rw_mutex &); // = delete
61 rw_mutex &operator=(const rw_mutex &); // = delete
62
63 pthread_rwlock_t _mutex;
64 };
65
66
67 template <typename Mutex>
68 class shared_lock {
69 public:
70 typedef Mutex mutex_type;
71
shared_lock()72 shared_lock() : _mutex(0), _owns_lock(false) {}
shared_lock(mutex_type & m)73 explicit shared_lock(mutex_type &m)
74 : _mutex(addressOf(m)), _owns_lock(false)
75 {
76 lock();
77 _owns_lock = true;
78 }
79
shared_lock(mutex_type & m,defer_lock_t)80 shared_lock(mutex_type &m, defer_lock_t)
81 : _mutex(addressOf(m)), _owns_lock(false)
82 {
83 }
84
shared_lock(mutex_type & m,try_to_lock_t)85 shared_lock(mutex_type &m, try_to_lock_t)
86 : _mutex(addressOf(m)), _owns_lock(_mutex->try_lock_shared())
87 {
88 }
89
shared_lock(mutex_type & m,adopt_lock_t)90 shared_lock(mutex_type &m, adopt_lock_t)
91 : _mutex(addressOf(m)), _owns_lock(true)
92 {
93 }
94
95 // template<typename Clock, typename Duration>
96 // shared_lock(mutex_type& m,
97 // const chrono::time_point<Clock, Duration>& atime);
98
99 // template<typename Rep, typename Period>
100 // shared_lock(mutex_type& m,
101 // const chrono::duration<Rep, Period>& rtime);
102
~shared_lock()103 ~shared_lock()
104 {
105 if (_owns_lock) unlock();
106 }
107
108 // shared_lock(shared_lock&& other)
109 // shared_lock& operator=(shared_lock&& other)
110
lock()111 void lock()
112 {
113 if (!_mutex) throw_system_error(EPERM);
114 if (_owns_lock) throw_system_error(EDEADLK);
115 _mutex->lock_shared();
116 _owns_lock = true;
117 }
118
try_lock()119 bool try_lock()
120 {
121 if (!_mutex) throw_system_error(EPERM);
122 if (_owns_lock) throw_system_error(EDEADLK);
123 _owns_lock = _mutex->try_lock_shared();
124 return _owns_lock;
125 }
126
127 // template<typename Clock, typename Duration>
128 // bool try_lock_until(const chrono::time_point<Clock, Duration>& atime)
129
130 // template<typename Rep, typename Period>
131 // bool try_lock_for(const chrono::duration<Rep, Period>& rtime)
132
unlock()133 void unlock()
134 {
135 if (!_owns_lock) throw_system_error(EPERM);
136 if (_mutex) {
137 _mutex->unlock_shared();
138 _owns_lock = false;
139 }
140 }
141
swap(shared_lock & other)142 void swap(shared_lock &other)
143 {
144 std::swap(_mutex, other._mutex);
145 std::swap(_owns_lock, other._owns_lock);
146 }
147
release()148 mutex_type *release()
149 {
150 mutex_type *ret = _mutex;
151 _mutex = 0;
152 _owns_lock = false;
153 return ret;
154 }
155
owns_lock()156 bool owns_lock() const { return _owns_lock; }
157 operator bool() const { return owns_lock(); } // explicit
mutex()158 mutex_type *mutex() const { return _mutex; }
159 private:
160 shared_lock(const shared_lock &); // = delete;
161 shared_lock &operator=(const shared_lock &); // = delete;
162
163 mutex_type *_mutex;
164 bool _owns_lock;
165
166 // basically std::addressof from C++11's <memory>
167 template <typename T>
addressOf(T & x)168 inline T *addressOf(T &x)
169 {
170 return reinterpret_cast<T *>(
171 &const_cast<char &>(reinterpret_cast<const volatile char &>(x)));
172 }
173 };
174
175
176 template <typename Mutex>
swap(shared_lock<Mutex> & x,shared_lock<Mutex> & y)177 void swap(shared_lock<Mutex> &x, shared_lock<Mutex> &y)
178 {
179 x.swap(y);
180 }
181
182 } // namespace mk
183
184 #endif // mk_SharedMutex_h
185