shared_mutex revision 278724
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                return false;
197            }
198        }
199    }
200    return true;
201}
202
203template <class _Clock, class _Duration>
204bool
205shared_timed_mutex::try_lock_shared_until(
206                        const chrono::time_point<_Clock, _Duration>& __abs_time)
207{
208    unique_lock<mutex> __lk(__mut_);
209    if ((__state_ & __write_entered_) || (__state_ & __n_readers_) == __n_readers_)
210    {
211        while (true)
212        {
213            cv_status status = __gate1_.wait_until(__lk, __abs_time);
214            if ((__state_ & __write_entered_) == 0 &&
215                                       (__state_ & __n_readers_) < __n_readers_)
216                break;
217            if (status == cv_status::timeout)
218                return false;
219        }
220    }
221    unsigned __num_readers = (__state_ & __n_readers_) + 1;
222    __state_ &= ~__n_readers_;
223    __state_ |= __num_readers;
224    return true;
225}
226
227template <class _Mutex>
228class shared_lock
229{
230public:
231    typedef _Mutex mutex_type;
232
233private:
234    mutex_type* __m_;
235    bool __owns_;
236
237public:
238    _LIBCPP_INLINE_VISIBILITY
239    shared_lock() _NOEXCEPT
240        : __m_(nullptr),
241          __owns_(false)
242        {}
243
244    _LIBCPP_INLINE_VISIBILITY
245    explicit shared_lock(mutex_type& __m)
246        : __m_(&__m),
247          __owns_(true)
248        {__m_->lock_shared();}
249
250    _LIBCPP_INLINE_VISIBILITY
251    shared_lock(mutex_type& __m, defer_lock_t) _NOEXCEPT
252        : __m_(&__m),
253          __owns_(false)
254        {}
255
256    _LIBCPP_INLINE_VISIBILITY
257    shared_lock(mutex_type& __m, try_to_lock_t)
258        : __m_(&__m),
259          __owns_(__m.try_lock_shared())
260        {}
261
262    _LIBCPP_INLINE_VISIBILITY
263    shared_lock(mutex_type& __m, adopt_lock_t)
264        : __m_(&__m),
265          __owns_(true)
266        {}
267
268    template <class _Clock, class _Duration>
269        _LIBCPP_INLINE_VISIBILITY
270        shared_lock(mutex_type& __m,
271                    const chrono::time_point<_Clock, _Duration>& __abs_time)
272            : __m_(&__m),
273              __owns_(__m.try_lock_shared_until(__abs_time))
274            {}
275
276    template <class _Rep, class _Period>
277        _LIBCPP_INLINE_VISIBILITY
278        shared_lock(mutex_type& __m,
279                    const chrono::duration<_Rep, _Period>& __rel_time)
280            : __m_(&__m),
281              __owns_(__m.try_lock_shared_for(__rel_time))
282            {}
283
284    _LIBCPP_INLINE_VISIBILITY
285    ~shared_lock()
286    {
287        if (__owns_)
288            __m_->unlock_shared();
289    }
290
291    shared_lock(shared_lock const&) = delete;
292    shared_lock& operator=(shared_lock const&) = delete;
293
294    _LIBCPP_INLINE_VISIBILITY
295    shared_lock(shared_lock&& __u) _NOEXCEPT
296        : __m_(__u.__m_),
297          __owns_(__u.__owns_)
298        {
299            __u.__m_ = nullptr;
300            __u.__owns_ = false;
301        }
302
303    _LIBCPP_INLINE_VISIBILITY
304    shared_lock& operator=(shared_lock&& __u) _NOEXCEPT
305    {
306        if (__owns_)
307            __m_->unlock_shared();
308        __m_ = nullptr;
309        __owns_ = false;
310        __m_ = __u.__m_;
311        __owns_ = __u.__owns_;
312        __u.__m_ = nullptr;
313        __u.__owns_ = false;
314        return *this;
315    }
316
317    void lock();
318    bool try_lock();
319    template <class Rep, class Period>
320        bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);
321    template <class Clock, class Duration>
322        bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);
323    void unlock();
324
325    // Setters
326    _LIBCPP_INLINE_VISIBILITY
327    void swap(shared_lock& __u) _NOEXCEPT
328    {
329        _VSTD::swap(__m_, __u.__m_);
330        _VSTD::swap(__owns_, __u.__owns_);
331    }
332
333    _LIBCPP_INLINE_VISIBILITY
334    mutex_type* release() _NOEXCEPT
335    {
336        mutex_type* __m = __m_;
337        __m_ = nullptr;
338        __owns_ = false;
339        return __m;
340    }
341
342    // Getters
343    _LIBCPP_INLINE_VISIBILITY
344    bool owns_lock() const _NOEXCEPT {return __owns_;}
345
346    _LIBCPP_INLINE_VISIBILITY
347    explicit operator bool () const _NOEXCEPT {return __owns_;}
348
349    _LIBCPP_INLINE_VISIBILITY
350    mutex_type* mutex() const _NOEXCEPT {return __m_;}
351};
352
353template <class _Mutex>
354void
355shared_lock<_Mutex>::lock()
356{
357    if (__m_ == nullptr)
358        __throw_system_error(EPERM, "shared_lock::lock: references null mutex");
359    if (__owns_)
360        __throw_system_error(EDEADLK, "shared_lock::lock: already locked");
361    __m_->lock_shared();
362    __owns_ = true;
363}
364
365template <class _Mutex>
366bool
367shared_lock<_Mutex>::try_lock()
368{
369    if (__m_ == nullptr)
370        __throw_system_error(EPERM, "shared_lock::try_lock: references null mutex");
371    if (__owns_)
372        __throw_system_error(EDEADLK, "shared_lock::try_lock: already locked");
373    __owns_ = __m_->try_lock_shared();
374    return __owns_;
375}
376
377template <class _Mutex>
378template <class _Rep, class _Period>
379bool
380shared_lock<_Mutex>::try_lock_for(const chrono::duration<_Rep, _Period>& __d)
381{
382    if (__m_ == nullptr)
383        __throw_system_error(EPERM, "shared_lock::try_lock_for: references null mutex");
384    if (__owns_)
385        __throw_system_error(EDEADLK, "shared_lock::try_lock_for: already locked");
386    __owns_ = __m_->try_lock_shared_for(__d);
387    return __owns_;
388}
389
390template <class _Mutex>
391template <class _Clock, class _Duration>
392bool
393shared_lock<_Mutex>::try_lock_until(const chrono::time_point<_Clock, _Duration>& __t)
394{
395    if (__m_ == nullptr)
396        __throw_system_error(EPERM, "shared_lock::try_lock_until: references null mutex");
397    if (__owns_)
398        __throw_system_error(EDEADLK, "shared_lock::try_lock_until: already locked");
399    __owns_ = __m_->try_lock_shared_until(__t);
400    return __owns_;
401}
402
403template <class _Mutex>
404void
405shared_lock<_Mutex>::unlock()
406{
407    if (!__owns_)
408        __throw_system_error(EPERM, "shared_lock::unlock: not locked");
409    __m_->unlock_shared();
410    __owns_ = false;
411}
412
413template <class _Mutex>
414inline _LIBCPP_INLINE_VISIBILITY
415void
416swap(shared_lock<_Mutex>& __x, shared_lock<_Mutex>& __y) _NOEXCEPT
417    {__x.swap(__y);}
418
419_LIBCPP_END_NAMESPACE_STD
420
421#endif  // !_LIBCPP_HAS_NO_THREADS
422
423#endif  // _LIBCPP_STD_VER > 11
424
425#endif  // _LIBCPP_SHARED_MUTEX
426