shared_mutex revision 262801
1// -*- C++ -*-
2//===------------------------ shared_mutex --------------------------------===//
3//
4//                     The LLVM Compiler Infrastructure
5//
6// This file is dual licensed under the MIT and the University of Illinois Open
7// Source Licenses. See LICENSE.TXT for details.
8//
9//===----------------------------------------------------------------------===//
10
11#ifndef _LIBCPP_SHARED_MUTEX
12#define _LIBCPP_SHARED_MUTEX
13
14/*
15    shared_mutex synopsis
16
17// C++1y
18
19namespace std
20{
21
22class shared_mutex
23{
24public:
25    shared_mutex();
26    ~shared_mutex();
27
28    shared_mutex(const shared_mutex&) = delete;
29    shared_mutex& operator=(const shared_mutex&) = delete;
30
31    // Exclusive ownership
32    void lock(); // blocking
33    bool try_lock();
34    template <class Rep, class Period>
35        bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);
36    template <class Clock, class Duration>
37        bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);
38    void unlock();
39
40    // Shared ownership
41    void lock_shared(); // blocking
42    bool try_lock_shared();
43    template <class Rep, class Period>
44        bool
45        try_lock_shared_for(const chrono::duration<Rep, Period>& rel_time);
46    template <class Clock, class Duration>
47        bool
48        try_lock_shared_until(const chrono::time_point<Clock, Duration>& abs_time);
49    void unlock_shared();
50};
51
52template <class Mutex>
53class shared_lock
54{
55public:
56    typedef Mutex mutex_type;
57
58    // Shared locking
59    shared_lock() noexcept;
60    explicit shared_lock(mutex_type& m); // blocking
61    shared_lock(mutex_type& m, defer_lock_t) noexcept;
62    shared_lock(mutex_type& m, try_to_lock_t);
63    shared_lock(mutex_type& m, adopt_lock_t);
64    template <class Clock, class Duration>
65        shared_lock(mutex_type& m,
66                    const chrono::time_point<Clock, Duration>& abs_time);
67    template <class Rep, class Period>
68        shared_lock(mutex_type& m,
69                    const chrono::duration<Rep, Period>& rel_time);
70    ~shared_lock();
71
72    shared_lock(shared_lock const&) = delete;
73    shared_lock& operator=(shared_lock const&) = delete;
74
75    shared_lock(shared_lock&& u) noexcept;
76    shared_lock& operator=(shared_lock&& u) noexcept;
77
78    void lock(); // blocking
79    bool try_lock();
80    template <class Rep, class Period>
81        bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);
82    template <class Clock, class Duration>
83        bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);
84    void unlock();
85
86    // Setters
87    void swap(shared_lock& u) noexcept;
88    mutex_type* release() noexcept;
89
90    // Getters
91    bool owns_lock() const noexcept;
92    explicit operator bool () const noexcept;
93    mutex_type* mutex() const noexcept;
94};
95
96template <class Mutex>
97    void swap(shared_lock<Mutex>& x, shared_lock<Mutex>& y) noexcept;
98
99}  // std
100
101*/
102
103#include <__config>
104
105#if _LIBCPP_STD_VER > 11 || defined(_LIBCPP_BUILDING_SHARED_MUTEX)
106
107#include <__mutex_base>
108
109#include <__undef_min_max>
110
111#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
112#pragma GCC system_header
113#endif
114
115_LIBCPP_BEGIN_NAMESPACE_STD
116
117class _LIBCPP_TYPE_VIS shared_mutex
118{
119    mutex               __mut_;
120    condition_variable  __gate1_;
121    condition_variable  __gate2_;
122    unsigned            __state_;
123
124    static const unsigned __write_entered_ = 1U << (sizeof(unsigned)*__CHAR_BIT__ - 1);
125    static const unsigned __n_readers_ = ~__write_entered_;
126public:
127    shared_mutex();
128    _LIBCPP_INLINE_VISIBILITY ~shared_mutex() = default;
129
130    shared_mutex(const shared_mutex&) = delete;
131    shared_mutex& operator=(const shared_mutex&) = delete;
132
133    // Exclusive ownership
134    void lock();
135    bool try_lock();
136    template <class _Rep, class _Period>
137        _LIBCPP_INLINE_VISIBILITY
138        bool
139        try_lock_for(const chrono::duration<_Rep, _Period>& __rel_time)
140        {
141            return try_lock_until(chrono::steady_clock::now() + __rel_time);
142        }
143    template <class _Clock, class _Duration>
144        bool
145        try_lock_until(const chrono::time_point<_Clock, _Duration>& __abs_time);
146    void unlock();
147
148    // Shared ownership
149    void lock_shared();
150    bool try_lock_shared();
151    template <class _Rep, class _Period>
152        _LIBCPP_INLINE_VISIBILITY
153        bool
154        try_lock_shared_for(const chrono::duration<_Rep, _Period>& __rel_time)
155        {
156            return try_lock_shared_until(chrono::steady_clock::now() + __rel_time);
157        }
158    template <class _Clock, class _Duration>
159        bool
160        try_lock_shared_until(const chrono::time_point<_Clock, _Duration>& __abs_time);
161    void unlock_shared();
162};
163
164template <class _Clock, class _Duration>
165bool
166shared_mutex::try_lock_until(
167                        const chrono::time_point<_Clock, _Duration>& __abs_time)
168{
169    unique_lock<mutex> __lk(__mut_);
170    if (__state_ & __write_entered_)
171    {
172        while (true)
173        {
174            cv_status __status = __gate1_.wait_until(__lk, __abs_time);
175            if ((__state_ & __write_entered_) == 0)
176                break;
177            if (__status == cv_status::timeout)
178                return false;
179        }
180    }
181    __state_ |= __write_entered_;
182    if (__state_ & __n_readers_)
183    {
184        while (true)
185        {
186            cv_status __status = __gate2_.wait_until(__lk, __abs_time);
187            if ((__state_ & __n_readers_) == 0)
188                break;
189            if (__status == cv_status::timeout)
190            {
191                __state_ &= ~__write_entered_;
192                return false;
193            }
194        }
195    }
196    return true;
197}
198
199template <class _Clock, class _Duration>
200bool
201shared_mutex::try_lock_shared_until(
202                        const chrono::time_point<_Clock, _Duration>& __abs_time)
203{
204    unique_lock<mutex> __lk(__mut_);
205    if ((__state_ & __write_entered_) || (__state_ & __n_readers_) == __n_readers_)
206    {
207        while (true)
208        {
209            cv_status status = __gate1_.wait_until(__lk, __abs_time);
210            if ((__state_ & __write_entered_) == 0 &&
211                                       (__state_ & __n_readers_) < __n_readers_)
212                break;
213            if (status == cv_status::timeout)
214                return false;
215        }
216    }
217    unsigned __num_readers = (__state_ & __n_readers_) + 1;
218    __state_ &= ~__n_readers_;
219    __state_ |= __num_readers;
220    return true;
221}
222
223template <class _Mutex>
224class shared_lock
225{
226public:
227    typedef _Mutex mutex_type;
228
229private:
230    mutex_type* __m_;
231    bool __owns_;
232
233public:
234    _LIBCPP_INLINE_VISIBILITY
235    shared_lock() noexcept
236        : __m_(nullptr),
237          __owns_(false)
238        {}
239
240    _LIBCPP_INLINE_VISIBILITY
241    explicit shared_lock(mutex_type& __m)
242        : __m_(&__m),
243          __owns_(true)
244        {__m_->lock_shared();}
245
246    _LIBCPP_INLINE_VISIBILITY
247    shared_lock(mutex_type& __m, defer_lock_t) noexcept
248        : __m_(&__m),
249          __owns_(false)
250        {}
251
252    _LIBCPP_INLINE_VISIBILITY
253    shared_lock(mutex_type& __m, try_to_lock_t)
254        : __m_(&__m),
255          __owns_(__m.try_lock_shared())
256        {}
257
258    _LIBCPP_INLINE_VISIBILITY
259    shared_lock(mutex_type& __m, adopt_lock_t)
260        : __m_(&__m),
261          __owns_(true)
262        {}
263
264    template <class _Clock, class _Duration>
265        _LIBCPP_INLINE_VISIBILITY
266        shared_lock(mutex_type& __m,
267                    const chrono::time_point<_Clock, _Duration>& __abs_time)
268            : __m_(&__m),
269              __owns_(__m.try_lock_shared_until(__abs_time))
270            {}
271
272    template <class _Rep, class _Period>
273        _LIBCPP_INLINE_VISIBILITY
274        shared_lock(mutex_type& __m,
275                    const chrono::duration<_Rep, _Period>& __rel_time)
276            : __m_(&__m),
277              __owns_(__m.try_lock_shared_for(__rel_time))
278            {}
279
280    _LIBCPP_INLINE_VISIBILITY
281    ~shared_lock()
282    {
283        if (__owns_)
284            __m_->unlock_shared();
285    }
286
287    shared_lock(shared_lock const&) = delete;
288    shared_lock& operator=(shared_lock const&) = delete;
289
290    _LIBCPP_INLINE_VISIBILITY
291    shared_lock(shared_lock&& __u) noexcept
292        : __m_(__u.__m_),
293          __owns_(__u.__owns_)
294        {
295            __u.__m_ = nullptr;
296            __u.__owns_ = false;
297        }
298
299    _LIBCPP_INLINE_VISIBILITY
300    shared_lock& operator=(shared_lock&& __u) noexcept
301    {
302        if (__owns_)
303            __m_->unlock_shared();
304        __m_ = nullptr;
305        __owns_ = false;
306        __m_ = __u.__m_;
307        __owns_ = __u.__owns_;
308        __u.__m_ = nullptr;
309        __u.__owns_ = false;
310        return *this;
311    }
312
313    void lock();
314    bool try_lock();
315    template <class Rep, class Period>
316        bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);
317    template <class Clock, class Duration>
318        bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);
319    void unlock();
320
321    // Setters
322    _LIBCPP_INLINE_VISIBILITY
323    void swap(shared_lock& __u) noexcept
324    {
325        _VSTD::swap(__m_, __u.__m_);
326        _VSTD::swap(__owns_, __u.__owns_);
327    }
328
329    _LIBCPP_INLINE_VISIBILITY
330    mutex_type* release() noexcept
331    {
332        mutex_type* __m = __m_;
333        __m_ = nullptr;
334        __owns_ = false;
335        return __m;
336    }
337
338    // Getters
339    _LIBCPP_INLINE_VISIBILITY
340    bool owns_lock() const noexcept {return __owns_;}
341
342    _LIBCPP_INLINE_VISIBILITY
343    explicit operator bool () const noexcept {return __owns_;}
344
345    _LIBCPP_INLINE_VISIBILITY
346    mutex_type* mutex() const noexcept {return __m_;}
347};
348
349template <class _Mutex>
350void
351shared_lock<_Mutex>::lock()
352{
353    if (__m_ == nullptr)
354        __throw_system_error(EPERM, "shared_lock::lock: references null mutex");
355    if (__owns_)
356        __throw_system_error(EDEADLK, "shared_lock::lock: already locked");
357    __m_->lock_shared();
358    __owns_ = true;
359}
360
361template <class _Mutex>
362bool
363shared_lock<_Mutex>::try_lock()
364{
365    if (__m_ == nullptr)
366        __throw_system_error(EPERM, "shared_lock::try_lock: references null mutex");
367    if (__owns_)
368        __throw_system_error(EDEADLK, "shared_lock::try_lock: already locked");
369    __owns_ = __m_->try_lock_shared();
370    return __owns_;
371}
372
373template <class _Mutex>
374template <class _Rep, class _Period>
375bool
376shared_lock<_Mutex>::try_lock_for(const chrono::duration<_Rep, _Period>& __d)
377{
378    if (__m_ == nullptr)
379        __throw_system_error(EPERM, "shared_lock::try_lock_for: references null mutex");
380    if (__owns_)
381        __throw_system_error(EDEADLK, "shared_lock::try_lock_for: already locked");
382    __owns_ = __m_->try_lock_shared_for(__d);
383    return __owns_;
384}
385
386template <class _Mutex>
387template <class _Clock, class _Duration>
388bool
389shared_lock<_Mutex>::try_lock_until(const chrono::time_point<_Clock, _Duration>& __t)
390{
391    if (__m_ == nullptr)
392        __throw_system_error(EPERM, "shared_lock::try_lock_until: references null mutex");
393    if (__owns_)
394        __throw_system_error(EDEADLK, "shared_lock::try_lock_until: already locked");
395    __owns_ = __m_->try_lock_shared_until(__t);
396    return __owns_;
397}
398
399template <class _Mutex>
400void
401shared_lock<_Mutex>::unlock()
402{
403    if (!__owns_)
404        __throw_system_error(EPERM, "shared_lock::unlock: not locked");
405    __m_->unlock_shared();
406    __owns_ = false;
407}
408
409template <class _Mutex>
410inline _LIBCPP_INLINE_VISIBILITY
411void
412swap(shared_lock<_Mutex>& __x, shared_lock<_Mutex>& __y) noexcept
413    {__x.swap(__y);}
414
415_LIBCPP_END_NAMESPACE_STD
416
417#endif  // _LIBCPP_STD_VER > 11
418
419#endif  // _LIBCPP_SHARED_MUTEX
420