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