Coverage Report

Created: 2022-08-24 06:19

/src/Fast-DDS/include/fastrtps/utils/shared_mutex.hpp
Line
Count
Source (jump to first uncovered line)
1
// Copyright Howard Hinnant 2007-2010. Distributed under the Boost
2
// Software License, Version 1.0. (see http://www.boost.org/LICENSE_1_0.txt)
3
4
/**
5
 * @file shared_mutex.hpp
6
 */
7
8
#ifndef _UTILS_SHARED_MUTEX_HPP_
9
#define _UTILS_SHARED_MUTEX_HPP_
10
11
#ifndef USE_THIRDPARTY_SHARED_MUTEX
12
#   if defined(_MSC_VER) && _MSVC_LANG < 202302L
13
#       pragma message("warning: USE_THIRDPARTY_SHARED_MUTEX not defined. By default use framework version.")
14
#   else
15
#       warning "USE_THIRDPARTY_SHARED_MUTEX not defined. By default use framework version."
16
#   endif // if defined(_MSC_VER) && _MSVC_LANG < 202302L
17
#   define USE_THIRDPARTY_SHARED_MUTEX 0
18
#endif // ifndef USE_THIRDPARTY_SHARED_MUTEX
19
20
#if defined(__has_include) && __has_include(<version>)
21
#   include <version>
22
#endif // if defined(__has_include) && __has_include(<version>)
23
24
// Detect if the share_mutex feature is available
25
#if defined(__has_include) && __has_include(<version>) && !defined(__cpp_lib_shared_mutex) || \
26
    /* allow users to ignore shared_mutex framework implementation */ \
27
    (~USE_THIRDPARTY_SHARED_MUTEX + 1) || \
28
    /* deprecated procedure if the good one is not available*/ \
29
    ( !(defined(__has_include) && __has_include(<version>)) && \
30
    !(defined(HAVE_CXX17) && HAVE_CXX17) &&  __cplusplus < 201703 )
31
32
#include <mutex>
33
#include <condition_variable>
34
#include <climits>
35
#include <system_error>
36
37
namespace eprosima {
38
39
class shared_mutex
40
{
41
    typedef std::mutex mutex_t;
42
    typedef std::condition_variable cond_t;
43
    typedef unsigned count_t;
44
45
    mutex_t mut_;
46
    cond_t gate1_;
47
    cond_t gate2_;
48
    count_t state_;
49
50
    static const count_t write_entered_ = 1U << (sizeof(count_t) * CHAR_BIT - 1);
51
    static const count_t n_readers_ = ~write_entered_;
52
53
public:
54
55
    shared_mutex()
56
        : state_(0)
57
1.94k
    {
58
1.94k
    }
59
60
    ~shared_mutex()
61
1.94k
    {
62
1.94k
        std::lock_guard<mutex_t> _(mut_);
63
1.94k
    }
64
65
    shared_mutex(
66
            const shared_mutex&) = delete;
67
    shared_mutex& operator =(
68
            const shared_mutex&) = delete;
69
70
    // Exclusive ownership
71
72
    void lock()
73
7.36k
    {
74
7.36k
        std::unique_lock<mutex_t> lk(mut_);
75
7.36k
        while (state_ & write_entered_)
76
0
        {
77
0
            gate1_.wait(lk);
78
0
        }
79
7.36k
        state_ |= write_entered_;
80
7.36k
        while (state_ & n_readers_)
81
0
        {
82
0
            gate2_.wait(lk);
83
0
        }
84
7.36k
    }
85
86
    bool try_lock()
87
0
    {
88
0
        std::unique_lock<mutex_t> lk(mut_);
89
0
        if (state_ == 0)
90
0
        {
91
0
            state_ = write_entered_;
92
0
            return true;
93
0
        }
94
0
        return false;
95
0
    }
96
97
    void unlock()
98
7.36k
    {
99
7.36k
        std::lock_guard<mutex_t> _(mut_);
100
7.36k
        state_ = 0;
101
7.36k
        gate1_.notify_all();
102
7.36k
    }
103
104
    // Shared ownership
105
106
    void lock_shared()
107
27.1k
    {
108
27.1k
        std::unique_lock<mutex_t> lk(mut_);
109
27.1k
        while ((state_ & write_entered_) || (state_ & n_readers_) == n_readers_)
110
0
        {
111
0
            gate1_.wait(lk);
112
0
        }
113
27.1k
        count_t num_readers = (state_ & n_readers_) + 1;
114
27.1k
        state_ &= ~n_readers_;
115
27.1k
        state_ |= num_readers;
116
27.1k
    }
117
118
    bool try_lock_shared()
119
0
    {
120
0
        std::unique_lock<mutex_t> lk(mut_);
121
0
        count_t num_readers = state_ & n_readers_;
122
0
        if (!(state_ & write_entered_) && num_readers != n_readers_)
123
0
        {
124
0
            ++num_readers;
125
0
            state_ &= ~n_readers_;
126
0
            state_ |= num_readers;
127
0
            return true;
128
0
        }
129
0
        return false;
130
0
    }
131
132
    void unlock_shared()
133
27.1k
    {
134
27.1k
        std::lock_guard<mutex_t> _(mut_);
135
27.1k
        count_t num_readers = (state_ & n_readers_) - 1;
136
27.1k
        state_ &= ~n_readers_;
137
27.1k
        state_ |= num_readers;
138
27.1k
        if (state_ & write_entered_)
139
0
        {
140
0
            if (num_readers == 0)
141
0
            {
142
0
                gate2_.notify_one();
143
0
            }
144
0
        }
145
27.1k
        else
146
27.1k
        {
147
27.1k
            if (num_readers == n_readers_ - 1)
148
0
            {
149
0
                gate1_.notify_one();
150
0
            }
151
27.1k
        }
152
27.1k
    }
153
154
};
155
156
template <class Mutex>
157
class shared_lock
158
{
159
public:
160
161
    typedef Mutex mutex_type;
162
163
private:
164
165
    mutex_type* m_;
166
    bool owns_;
167
168
    struct __nat
169
    {
170
        int _;
171
    };
172
173
public:
174
175
    shared_lock()
176
        : m_(nullptr)
177
        , owns_(false)
178
0
    {
179
0
    }
180
181
    explicit shared_lock(
182
            mutex_type& m)
183
        : m_(&m)
184
        , owns_(true)
185
27.1k
    {
186
27.1k
        m_->lock_shared();
187
27.1k
    }
188
189
    shared_lock(
190
            mutex_type& m,
191
            std::defer_lock_t)
192
        : m_(&m)
193
        , owns_(false)
194
    {
195
    }
196
197
    shared_lock(
198
            mutex_type& m,
199
            std::try_to_lock_t)
200
        : m_(&m)
201
        , owns_(m.try_lock_shared())
202
    {
203
    }
204
205
    shared_lock(
206
            mutex_type& m,
207
            std::adopt_lock_t)
208
        : m_(&m)
209
        , owns_(true)
210
    {
211
    }
212
213
    template <class Clock, class Duration>
214
    shared_lock(
215
            mutex_type& m,
216
            const std::chrono::time_point<Clock, Duration>& abs_time)
217
        : m_(&m)
218
        , owns_(m.try_lock_shared_until(abs_time))
219
    {
220
    }
221
222
    template <class Rep, class Period>
223
    shared_lock(
224
            mutex_type& m,
225
            const std::chrono::duration<Rep, Period>& rel_time)
226
        : m_(&m)
227
        , owns_(m.try_lock_shared_for(rel_time))
228
    {
229
    }
230
231
    ~shared_lock()
232
27.1k
    {
233
27.1k
        if (owns_)
234
27.1k
        {
235
27.1k
            m_->unlock_shared();
236
27.1k
        }
237
27.1k
    }
238
239
    shared_lock(
240
            shared_lock const&) = delete;
241
    shared_lock& operator =(
242
            shared_lock const&) = delete;
243
244
    shared_lock(
245
            shared_lock&& sl)
246
        : m_(sl.m_)
247
        , owns_(sl.owns_)
248
    {
249
        sl.m_ = nullptr; sl.owns_ = false;
250
    }
251
252
    shared_lock& operator =(
253
            shared_lock&& sl)
254
0
    {
255
0
        if (owns_)
256
0
        {
257
0
            m_->unlock_shared();
258
0
        }
259
0
        m_ = sl.m_;
260
0
        owns_ = sl.owns_;
261
0
        sl.m_ = nullptr;
262
0
        sl.owns_ = false;
263
0
        return *this;
264
0
    }
265
266
    explicit shared_lock(
267
            std::unique_lock<mutex_type>&& ul)
268
        : m_(ul.mutex())
269
        , owns_(ul.owns_lock())
270
    {
271
        if (owns_)
272
        {
273
            m_->unlock_and_lock_shared();
274
        }
275
        ul.release();
276
    }
277
278
    void lock();
279
    bool try_lock();
280
    template <class Rep, class Period>
281
    bool try_lock_for(
282
            const std::chrono::duration<Rep, Period>& rel_time)
283
    {
284
        return try_lock_until(std::chrono::steady_clock::now() + rel_time);
285
    }
286
287
    template <class Clock, class Duration>
288
    bool
289
    try_lock_until(
290
            const std::chrono::time_point<Clock, Duration>& abs_time);
291
    void unlock();
292
293
    void swap(
294
            shared_lock&& u)
295
    {
296
        std::swap(m_, u.m_);
297
        std::swap(owns_, u.owns_);
298
    }
299
300
    mutex_type* release()
301
    {
302
        mutex_type* r = m_;
303
        m_ = nullptr;
304
        owns_ = false;
305
        return r;
306
    }
307
308
    bool owns_lock() const
309
    {
310
        return owns_;
311
    }
312
313
    operator int __nat::* () const {
314
        return owns_ ? &__nat::_ : 0;
315
    }
316
    mutex_type* mutex() const
317
    {
318
        return m_;
319
    }
320
321
};
322
323
template <class Mutex>
324
void
325
shared_lock<Mutex>::lock()
326
{
327
    if (m_ == nullptr)
328
    {
329
        throw std::system_error(std::error_code(EPERM, std::system_category()),
330
                      "shared_lock::lock: references null mutex");
331
    }
332
    if (owns_)
333
    {
334
        throw std::system_error(std::error_code(EDEADLK, std::system_category()),
335
                      "shared_lock::lock: already locked");
336
    }
337
    m_->lock_shared();
338
    owns_ = true;
339
}
340
341
template <class Mutex>
342
bool
343
shared_lock<Mutex>::try_lock()
344
{
345
    if (m_ == nullptr)
346
    {
347
        throw std::system_error(std::error_code(EPERM, std::system_category()),
348
                      "shared_lock::try_lock: references null mutex");
349
    }
350
    if (owns_)
351
    {
352
        throw std::system_error(std::error_code(EDEADLK, std::system_category()),
353
                      "shared_lock::try_lock: already locked");
354
    }
355
    owns_ = m_->try_lock_shared();
356
    return owns_;
357
}
358
359
} //namespace eprosima
360
361
#else // fallback to STL
362
363
#include <shared_mutex>
364
365
namespace eprosima {
366
367
using std::shared_mutex;
368
using std::shared_lock;
369
370
} //namespace eprosima
371
372
#endif // if (__cplusplus < 201402) || (defined(_MSC_VER) && _MSC_VER < 1900 )
373
374
#endif // _UTILS_SHARED_MUTEX_HPP_