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_timed_mutex
23{
24public:
25    shared_timed_mutex();
26    ~shared_timed_mutex();
27
28    shared_timed_mutex(const shared_timed_mutex&) = delete;
29    shared_timed_mutex& operator=(const shared_timed_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#if !_LIBCPP_SINGLE_THREADED
116
117_LIBCPP_BEGIN_NAMESPACE_STD
118
119class _LIBCPP_TYPE_VIS shared_timed_mutex
120{
121    mutex               __mut_;
122    condition_variable  __gate1_;
123    condition_variable  __gate2_;
124    unsigned            __state_;
125
126    static const unsigned __write_entered_ = 1U << (sizeof(unsigned)*__CHAR_BIT__ - 1);
127    static const unsigned __n_readers_ = ~__write_entered_;
128public:
129    shared_timed_mutex();
130    _LIBCPP_INLINE_VISIBILITY ~shared_timed_mutex() = default;
131
132    shared_timed_mutex(const shared_timed_mutex&) = delete;
133    shared_timed_mutex& operator=(const shared_timed_mutex&) = delete;
134
135    // Exclusive ownership
136    void lock();
137    bool try_lock();
138    template <class _Rep, class _Period>
139        _LIBCPP_INLINE_VISIBILITY
140        bool
141        try_lock_for(const chrono::duration<_Rep, _Period>& __rel_time)
142        {
143            return try_lock_until(chrono::steady_clock::now() + __rel_time);
144        }
145    template <class _Clock, class _Duration>
146        bool
147        try_lock_until(const chrono::time_point<_Clock, _Duration>& __abs_time);
148    void unlock();
149
150    // Shared ownership
151    void lock_shared();
152    bool try_lock_shared();
153    template <class _Rep, class _Period>
154        _LIBCPP_INLINE_VISIBILITY
155        bool
156        try_lock_shared_for(const chrono::duration<_Rep, _Period>& __rel_time)
157        {
158            return try_lock_shared_until(chrono::steady_clock::now() + __rel_time);
159        }
160    template <class _Clock, class _Duration>
161        bool
162        try_lock_shared_until(const chrono::time_point<_Clock, _Duration>& __abs_time);
163    void unlock_shared();
164};
165
166template <class _Clock, class _Duration>
167bool
168shared_timed_mutex::try_lock_until(
169                        const chrono::time_point<_Clock, _Duration>& __abs_time)
170{
171    unique_lock<mutex> __lk(__mut_);
172    if (__state_ & __write_entered_)
173    {
174        while (true)
175        {
176            cv_status __status = __gate1_.wait_until(__lk, __abs_time);
177            if ((__state_ & __write_entered_) == 0)
178                break;
179            if (__status == cv_status::timeout)
180                return false;
181        }
182    }
183    __state_ |= __write_entered_;
184    if (__state_ & __n_readers_)
185    {
186        while (true)
187        {
188            cv_status __status = __gate2_.wait_until(__lk, __abs_time);
189            if ((__state_ & __n_readers_) == 0)
190                break;
191            if (__status == cv_status::timeout)
192            {
193                __state_ &= ~__write_entered_;
194                return false;
195            }
196        }
197    }
198    return true;
199}
200
201template <class _Clock, class _Duration>
202bool
203shared_timed_mutex::try_lock_shared_until(
204                        const chrono::time_point<_Clock, _Duration>& __abs_time)
205{
206    unique_lock<mutex> __lk(__mut_);
207    if ((__state_ & __write_entered_) || (__state_ & __n_readers_) == __n_readers_)
208    {
209        while (true)
210        {
211            cv_status status = __gate1_.wait_until(__lk, __abs_time);
212            if ((__state_ & __write_entered_) == 0 &&
213                                       (__state_ & __n_readers_) < __n_readers_)
214                break;
215            if (status == cv_status::timeout)
216                return false;
217        }
218    }
219    unsigned __num_readers = (__state_ & __n_readers_) + 1;
220    __state_ &= ~__n_readers_;
221    __state_ |= __num_readers;
222    return true;
223}
224
225template <class _Mutex>
226class shared_lock
227{
228public:
229    typedef _Mutex mutex_type;
230
231private:
232    mutex_type* __m_;
233    bool __owns_;
234
235public:
236    _LIBCPP_INLINE_VISIBILITY
237    shared_lock() noexcept
238        : __m_(nullptr),
239          __owns_(false)
240        {}
241
242    _LIBCPP_INLINE_VISIBILITY
243    explicit shared_lock(mutex_type& __m)
244        : __m_(&__m),
245          __owns_(true)
246        {__m_->lock_shared();}
247
248    _LIBCPP_INLINE_VISIBILITY
249    shared_lock(mutex_type& __m, defer_lock_t) noexcept
250        : __m_(&__m),
251          __owns_(false)
252        {}
253
254    _LIBCPP_INLINE_VISIBILITY
255    shared_lock(mutex_type& __m, try_to_lock_t)
256        : __m_(&__m),
257          __owns_(__m.try_lock_shared())
258        {}
259
260    _LIBCPP_INLINE_VISIBILITY
261    shared_lock(mutex_type& __m, adopt_lock_t)
262        : __m_(&__m),
263          __owns_(true)
264        {}
265
266    template <class _Clock, class _Duration>
267        _LIBCPP_INLINE_VISIBILITY
268        shared_lock(mutex_type& __m,
269                    const chrono::time_point<_Clock, _Duration>& __abs_time)
270            : __m_(&__m),
271              __owns_(__m.try_lock_shared_until(__abs_time))
272            {}
273
274    template <class _Rep, class _Period>
275        _LIBCPP_INLINE_VISIBILITY
276        shared_lock(mutex_type& __m,
277                    const chrono::duration<_Rep, _Period>& __rel_time)
278            : __m_(&__m),
279              __owns_(__m.try_lock_shared_for(__rel_time))
280            {}
281
282    _LIBCPP_INLINE_VISIBILITY
283    ~shared_lock()
284    {
285        if (__owns_)
286            __m_->unlock_shared();
287    }
288
289    shared_lock(shared_lock const&) = delete;
290    shared_lock& operator=(shared_lock const&) = delete;
291
292    _LIBCPP_INLINE_VISIBILITY
293    shared_lock(shared_lock&& __u) noexcept
294        : __m_(__u.__m_),
295          __owns_(__u.__owns_)
296        {
297            __u.__m_ = nullptr;
298            __u.__owns_ = false;
299        }
300
301    _LIBCPP_INLINE_VISIBILITY
302    shared_lock& operator=(shared_lock&& __u) noexcept
303    {
304        if (__owns_)
305            __m_->unlock_shared();
306        __m_ = nullptr;
307        __owns_ = false;
308        __m_ = __u.__m_;
309        __owns_ = __u.__owns_;
310        __u.__m_ = nullptr;
311        __u.__owns_ = false;
312        return *this;
313    }
314
315    void lock();
316    bool try_lock();
317    template <class Rep, class Period>
318        bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);
319    template <class Clock, class Duration>
320        bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);
321    void unlock();
322
323    // Setters
324    _LIBCPP_INLINE_VISIBILITY
325    void swap(shared_lock& __u) noexcept
326    {
327        _VSTD::swap(__m_, __u.__m_);
328        _VSTD::swap(__owns_, __u.__owns_);
329    }
330
331    _LIBCPP_INLINE_VISIBILITY
332    mutex_type* release() noexcept
333    {
334        mutex_type* __m = __m_;
335        __m_ = nullptr;
336        __owns_ = false;
337        return __m;
338    }
339
340    // Getters
341    _LIBCPP_INLINE_VISIBILITY
342    bool owns_lock() const noexcept {return __owns_;}
343
344    _LIBCPP_INLINE_VISIBILITY
345    explicit operator bool () const noexcept {return __owns_;}
346
347    _LIBCPP_INLINE_VISIBILITY
348    mutex_type* mutex() const noexcept {return __m_;}
349};
350
351template <class _Mutex>
352void
353shared_lock<_Mutex>::lock()
354{
355    if (__m_ == nullptr)
356        __throw_system_error(EPERM, "shared_lock::lock: references null mutex");
357    if (__owns_)
358        __throw_system_error(EDEADLK, "shared_lock::lock: already locked");
359    __m_->lock_shared();
360    __owns_ = true;
361}
362
363template <class _Mutex>
364bool
365shared_lock<_Mutex>::try_lock()
366{
367    if (__m_ == nullptr)
368        __throw_system_error(EPERM, "shared_lock::try_lock: references null mutex");
369    if (__owns_)
370        __throw_system_error(EDEADLK, "shared_lock::try_lock: already locked");
371    __owns_ = __m_->try_lock_shared();
372    return __owns_;
373}
374
375template <class _Mutex>
376template <class _Rep, class _Period>
377bool
378shared_lock<_Mutex>::try_lock_for(const chrono::duration<_Rep, _Period>& __d)
379{
380    if (__m_ == nullptr)
381        __throw_system_error(EPERM, "shared_lock::try_lock_for: references null mutex");
382    if (__owns_)
383        __throw_system_error(EDEADLK, "shared_lock::try_lock_for: already locked");
384    __owns_ = __m_->try_lock_shared_for(__d);
385    return __owns_;
386}
387
388template <class _Mutex>
389template <class _Clock, class _Duration>
390bool
391shared_lock<_Mutex>::try_lock_until(const chrono::time_point<_Clock, _Duration>& __t)
392{
393    if (__m_ == nullptr)
394        __throw_system_error(EPERM, "shared_lock::try_lock_until: references null mutex");
395    if (__owns_)
396        __throw_system_error(EDEADLK, "shared_lock::try_lock_until: already locked");
397    __owns_ = __m_->try_lock_shared_until(__t);
398    return __owns_;
399}
400
401template <class _Mutex>
402void
403shared_lock<_Mutex>::unlock()
404{
405    if (!__owns_)
406        __throw_system_error(EPERM, "shared_lock::unlock: not locked");
407    __m_->unlock_shared();
408    __owns_ = false;
409}
410
411template <class _Mutex>
412inline _LIBCPP_INLINE_VISIBILITY
413void
414swap(shared_lock<_Mutex>& __x, shared_lock<_Mutex>& __y) noexcept
415    {__x.swap(__y);}
416
417_LIBCPP_END_NAMESPACE_STD
418
419#endif  // _LIBC_HAS_PTHREADS
420
421#endif  // _LIBCPP_STD_VER > 11
422
423#endif  // _LIBCPP_SHARED_MUTEX
424