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