Blame view

RIOT/sys/cpp11-compat/include/riot/mutex.hpp 7.63 KB
a752c7ab   elopes   add first test an...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
  /*
   * Copyright (C) 2015 Hamburg University of Applied Sciences (HAW)
   *
   * This file is subject to the terms and conditions of the GNU Lesser
   * General Public License v2.1. See the file LICENSE in the top level
   * directory for more details.
   */
  
  /**
   * @ingroup cpp11-compat
   * @{
   *
   * @file
   * @brief   C++11 mutex drop in replacement
   * @see     <a href="http://en.cppreference.com/w/cpp/thread/mutex">
   *            std::mutex, std::lock_guard and std::unique_lock
   *          </a>
   *
   * @author  Raphael Hiesgen <raphael.hiesgen (at) haw-hamburg.de>
   *
   * @}
   */
  
  #ifndef RIOT_MUTEX_HPP
  #define RIOT_MUTEX_HPP
  
  #include "mutex.h"
  
  #include <utility>
  #include <stdexcept>
  #include <system_error>
  
  namespace riot {
  
  /**
   * @brief C++11 complient implementation of mutex, uses the time point
   *              implemented in our chrono replacement instead of the specified
   *              one
   * @see   <a href="http://en.cppreference.com/w/cpp/thread/mutex">
   *          std::mutex
   *        </a>
   */
  class mutex {
  public:
    /**
     * The native handle type used by the mutex.
     */
    using native_handle_type = mutex_t*;
  
    inline constexpr mutex() noexcept : m_mtx{{0}} {}
    ~mutex();
  
    /**
     * @brief Lock the mutex.
     */
    void lock();
    /**
     * @brief Try to lock the mutex.
     * @return `true` if the mutex was locked, `false` otherwise.
     */
    bool try_lock() noexcept;
    /**
     * @brief Unlock the mutex.
     */
    void unlock() noexcept;
  
    /**
     * @brief Provides access to the native handle.
     * @return The native handle of the mutex.
     */
    inline native_handle_type native_handle() { return &m_mtx; }
  
  private:
    mutex(const mutex&);
    mutex& operator=(const mutex&);
  
    mutex_t m_mtx;
  };
  
  /**
   * @brief Tag type for defer lock strategy.
   */
  struct defer_lock_t {};
  /**
   * @brief Tag type for try lock strategy.
   */
  struct try_to_lock_t {};
  /**
   * @brief Tag type for adopt lock strategy.
   */
  struct adopt_lock_t {};
  
  /**
   * @brief Tag constant for defer lock strategy.
   */
  constexpr defer_lock_t defer_lock = defer_lock_t();
  /**
   * @brief Tag constant for try lock strategy.
   */
  constexpr try_to_lock_t try_to_lock = try_to_lock_t();
  /**
   * @brief Tag constant for adopt lock strategy.
   */
  constexpr adopt_lock_t adopt_lock = adopt_lock_t();
  
  /**
   * @brief C++11 complient implementation of unique lock
   * @see   <a href="http://en.cppreference.com/w/cpp/thread/lock_guard">
   *          std::lock_guard
   *        </a>
   */
  template <class Mutex>
  class lock_guard {
  public:
    /**
     * The type of Mutex used by the lock_guard.
     */
    using mutex_type = Mutex;
  
    /**
     * @brief Constructs a lock_gurad from a Mutex and locks it.
     */
    inline explicit lock_guard(mutex_type& mtx) : m_mtx(mtx) { m_mtx.lock(); }
    /**
     * @brief Constructs a lock_guard from a Mutex, acquireing ownership without
     *        locking it.
     */
    inline lock_guard(mutex_type& mtx, adopt_lock_t) : m_mtx{mtx} {}
    inline ~lock_guard() { m_mtx.unlock(); }
  
  private:
    mutex_type& m_mtx;
  };
  
  /**
   * @brief C++11 complient implementation of unique lock
   * @see   <a href="http://en.cppreference.com/w/cpp/thread/unique_lock">
   *          std::unique_lock
   *        </a>
   */
  template <class Mutex>
  class unique_lock {
  public:
    /**
     * The type of Mutex used by the lock.
     */
    using mutex_type = Mutex;
  
    inline unique_lock() noexcept : m_mtx{nullptr}, m_owns{false} {}
    /**
     * @brief Constructs a unique_lock from a Mutex and locks it.
     */
    inline explicit unique_lock(mutex_type& mtx) : m_mtx{&mtx}, m_owns{true} {
      m_mtx->lock();
    }
    /**
     * @brief Constructs a unique_lock from a Mutex but does not lock it.
     */
    inline unique_lock(mutex_type& mtx, defer_lock_t) noexcept : m_mtx{&mtx},
                                                                 m_owns{false} {}
    /**
     * @brief Constructs a unique_lock from a Mutex and tries to lock it.
     */
    inline unique_lock(mutex_type& mtx, try_to_lock_t)
        : m_mtx{&mtx}, m_owns{mtx.try_lock()} {}
    /**
     * @brief Constructs a unique_lock from a Mutex that is already owned by the
     *        thread.
     */
    inline unique_lock(mutex_type& mtx, adopt_lock_t)
        : m_mtx{&mtx}, m_owns{true} {}
    inline ~unique_lock() {
      if (m_owns) {
        m_mtx->unlock();
      }
    }
    /**
     * @brief Move constructor.
     */
    inline unique_lock(unique_lock&& lock) noexcept : m_mtx{lock.m_mtx},
                                                      m_owns{lock.m_owns} {
      lock.m_mtx = nullptr;
      lock.m_owns = false;
    }
    /**
     * @brief Move assignment operator.
     */
    inline unique_lock& operator=(unique_lock&& lock) noexcept {
      if (m_owns) {
        m_mtx->unlock();
      }
      m_mtx = lock.m_mtx;
      m_owns = lock.m_owns;
      lock.m_mtx = nullptr;
      lock.m_owns = false;
      return *this;
    }
  
    /**
     * @brief Locks the associated mutex.
     */
    void lock();
    /**
     * @brief Tries to lock the associated mutex.
     * @return `true` if the mutex has been locked successfully,
     *         `false` otherwise.
     */
    bool try_lock();
    /**
     * @brief Unlocks the associated mutex.
     */
    void unlock();
  
    /**
     * @brief Swap this unique_lock with another unique_lock.
     */
    inline void swap(unique_lock& lock) noexcept {
      std::swap(m_mtx, lock.m_mtx);
      std::swap(m_owns, lock.m_owns);
    }
  
    /**
     * @brief Disassociate this lock from its mutex. The caller is responsible to
     *        unlock the mutex if it was locked before.
     * @return A pointer to the associated mutex or `nullptr` if there was none.
     */
    inline mutex_type* release() noexcept {
      mutex_type* mtx = m_mtx;
      m_mtx = nullptr;
      m_owns = false;
      return mtx;
    }
  
    /**
     * @brief Query ownership of the associate mutex.
     * @return `true` if an associated mutex exists and the lock owns it,
     *         `false` otherwise.
     */
    inline bool owns_lock() const noexcept { return m_owns; }
    /**
     * @brief Operator to query the ownership of the associated mutex.
     * @return `true` if an associated mutex exists and the lock owns it,
     *         `false` otherwise.
     */
    inline explicit operator bool() const noexcept { return m_owns; }
    /**
     * @brief Provides access to the associated mutex.
     * @return A pointer to the associated mutex or nullptr it there was none.
     */
    inline mutex_type* mutex() const noexcept { return m_mtx; }
  
  private:
    unique_lock(unique_lock const&);
    unique_lock& operator=(unique_lock const&);
  
    mutex_type* m_mtx;
    bool m_owns;
  };
  
  template <class Mutex>
  void unique_lock<Mutex>::lock() {
    if (m_mtx == nullptr) {
      throw std::system_error(
        std::make_error_code(std::errc::operation_not_permitted),
        "References null mutex.");
    }
    if (m_owns) {
      throw std::system_error(
        std::make_error_code(std::errc::resource_deadlock_would_occur),
        "Already locked.");
    }
    m_mtx->lock();
    m_owns = true;
  }
  
  template <class Mutex>
  bool unique_lock<Mutex>::try_lock() {
    if (m_mtx == nullptr) {
      throw std::system_error(
        std::make_error_code(std::errc::operation_not_permitted),
        "References null mutex.");
    }
    if (m_owns) {
      throw std::system_error(
        std::make_error_code(std::errc::resource_deadlock_would_occur),
        "Already locked.");
    }
    m_owns = m_mtx->try_lock();
    return m_owns;
  }
  
  template <class Mutex>
  void unique_lock<Mutex>::unlock() {
    if (!m_owns) {
      throw std::system_error(
        std::make_error_code(std::errc::operation_not_permitted),
        "Mutex not locked.");
    }
    m_mtx->unlock();
    m_owns = false;
  }
  
  /**
   * @brief Swaps two mutexes.
   * @param[inout] lhs    Reference to one mutex.
   * @param[inout] rhs    Reference to the other mutex.
   */
  template <class Mutex>
  inline void swap(unique_lock<Mutex>& lhs, unique_lock<Mutex>& rhs) noexcept {
    lhs.swap(rhs);
  }
  
  } // namespace riot
  
  #endif // RIOT_MUTEX_HPP