xref: /freebsd/contrib/llvm-project/libcxx/include/shared_mutex (revision ccfd87fe2ac0e2e6aeb1911a7d7cce6712a8564f)
1// -*- C++ -*-
2//===----------------------------------------------------------------------===//
3//
4// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5// See https://llvm.org/LICENSE.txt for license information.
6// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7//
8//===----------------------------------------------------------------------===//
9
10#ifndef _LIBCPP_SHARED_MUTEX
11#define _LIBCPP_SHARED_MUTEX
12
13/*
14    shared_mutex synopsis
15
16// C++1y
17
18namespace std
19{
20
21class shared_mutex      // C++17
22{
23public:
24    shared_mutex();
25    ~shared_mutex();
26
27    shared_mutex(const shared_mutex&) = delete;
28    shared_mutex& operator=(const shared_mutex&) = delete;
29
30    // Exclusive ownership
31    void lock(); // blocking
32    bool try_lock();
33    void unlock();
34
35    // Shared ownership
36    void lock_shared(); // blocking
37    bool try_lock_shared();
38    void unlock_shared();
39
40    typedef implementation-defined native_handle_type; // See 30.2.3
41    native_handle_type native_handle(); // See 30.2.3
42};
43
44class shared_timed_mutex
45{
46public:
47    shared_timed_mutex();
48    ~shared_timed_mutex();
49
50    shared_timed_mutex(const shared_timed_mutex&) = delete;
51    shared_timed_mutex& operator=(const shared_timed_mutex&) = delete;
52
53    // Exclusive ownership
54    void lock(); // blocking
55    bool try_lock();
56    template <class Rep, class Period>
57        bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);
58    template <class Clock, class Duration>
59        bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);
60    void unlock();
61
62    // Shared ownership
63    void lock_shared(); // blocking
64    bool try_lock_shared();
65    template <class Rep, class Period>
66        bool
67        try_lock_shared_for(const chrono::duration<Rep, Period>& rel_time);
68    template <class Clock, class Duration>
69        bool
70        try_lock_shared_until(const chrono::time_point<Clock, Duration>& abs_time);
71    void unlock_shared();
72};
73
74template <class Mutex>
75class shared_lock
76{
77public:
78    typedef Mutex mutex_type;
79
80    // Shared locking
81    shared_lock() noexcept;
82    explicit shared_lock(mutex_type& m); // blocking
83    shared_lock(mutex_type& m, defer_lock_t) noexcept;
84    shared_lock(mutex_type& m, try_to_lock_t);
85    shared_lock(mutex_type& m, adopt_lock_t);
86    template <class Clock, class Duration>
87        shared_lock(mutex_type& m,
88                    const chrono::time_point<Clock, Duration>& abs_time);
89    template <class Rep, class Period>
90        shared_lock(mutex_type& m,
91                    const chrono::duration<Rep, Period>& rel_time);
92    ~shared_lock();
93
94    shared_lock(shared_lock const&) = delete;
95    shared_lock& operator=(shared_lock const&) = delete;
96
97    shared_lock(shared_lock&& u) noexcept;
98    shared_lock& operator=(shared_lock&& u) noexcept;
99
100    void lock(); // blocking
101    bool try_lock();
102    template <class Rep, class Period>
103        bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);
104    template <class Clock, class Duration>
105        bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);
106    void unlock();
107
108    // Setters
109    void swap(shared_lock& u) noexcept;
110    mutex_type* release() noexcept;
111
112    // Getters
113    bool owns_lock() const noexcept;
114    explicit operator bool () const noexcept;
115    mutex_type* mutex() const noexcept;
116};
117
118template <class Mutex>
119    void swap(shared_lock<Mutex>& x, shared_lock<Mutex>& y) noexcept;
120
121}  // std
122
123*/
124
125#include <__assert> // all public C++ headers provide the assertion handler
126#include <__availability>
127#include <__config>
128#include <version>
129
130_LIBCPP_PUSH_MACROS
131#include <__undef_macros>
132
133
134#if _LIBCPP_STD_VER > 11 || defined(_LIBCPP_BUILDING_LIBRARY)
135
136#include <__mutex_base>
137
138#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
139#  pragma GCC system_header
140#endif
141
142#ifdef _LIBCPP_HAS_NO_THREADS
143# error "<shared_mutex> is not supported since libc++ has been configured without support for threads."
144#endif
145
146_LIBCPP_BEGIN_NAMESPACE_STD
147
148struct _LIBCPP_TYPE_VIS _LIBCPP_AVAILABILITY_SHARED_MUTEX _LIBCPP_THREAD_SAFETY_ANNOTATION(capability("shared_mutex"))
149__shared_mutex_base
150{
151    mutex               __mut_;
152    condition_variable  __gate1_;
153    condition_variable  __gate2_;
154    unsigned            __state_;
155
156    static const unsigned __write_entered_ = 1U << (sizeof(unsigned)*__CHAR_BIT__ - 1);
157    static const unsigned __n_readers_ = ~__write_entered_;
158
159    __shared_mutex_base();
160    _LIBCPP_INLINE_VISIBILITY ~__shared_mutex_base() = default;
161
162    __shared_mutex_base(const __shared_mutex_base&) = delete;
163    __shared_mutex_base& operator=(const __shared_mutex_base&) = delete;
164
165    // Exclusive ownership
166    void lock() _LIBCPP_THREAD_SAFETY_ANNOTATION(acquire_capability()); // blocking
167    bool try_lock() _LIBCPP_THREAD_SAFETY_ANNOTATION(try_acquire_capability(true));
168    void unlock() _LIBCPP_THREAD_SAFETY_ANNOTATION(release_capability());
169
170    // Shared ownership
171    void lock_shared() _LIBCPP_THREAD_SAFETY_ANNOTATION(acquire_shared_capability()); // blocking
172    bool try_lock_shared() _LIBCPP_THREAD_SAFETY_ANNOTATION(try_acquire_shared_capability(true));
173    void unlock_shared() _LIBCPP_THREAD_SAFETY_ANNOTATION(release_shared_capability());
174
175//     typedef implementation-defined native_handle_type; // See 30.2.3
176//     native_handle_type native_handle(); // See 30.2.3
177};
178
179
180#if _LIBCPP_STD_VER > 14
181class _LIBCPP_TYPE_VIS _LIBCPP_AVAILABILITY_SHARED_MUTEX shared_mutex
182{
183    __shared_mutex_base __base_;
184public:
185    _LIBCPP_INLINE_VISIBILITY shared_mutex() : __base_() {}
186    _LIBCPP_INLINE_VISIBILITY ~shared_mutex() = default;
187
188    shared_mutex(const shared_mutex&) = delete;
189    shared_mutex& operator=(const shared_mutex&) = delete;
190
191    // Exclusive ownership
192    _LIBCPP_INLINE_VISIBILITY void lock()     { return __base_.lock(); }
193    _LIBCPP_INLINE_VISIBILITY bool try_lock() { return __base_.try_lock(); }
194    _LIBCPP_INLINE_VISIBILITY void unlock()   { return __base_.unlock(); }
195
196    // Shared ownership
197    _LIBCPP_INLINE_VISIBILITY void lock_shared()     { return __base_.lock_shared(); }
198    _LIBCPP_INLINE_VISIBILITY bool try_lock_shared() { return __base_.try_lock_shared(); }
199    _LIBCPP_INLINE_VISIBILITY void unlock_shared()   { return __base_.unlock_shared(); }
200
201//     typedef __shared_mutex_base::native_handle_type native_handle_type;
202//     _LIBCPP_INLINE_VISIBILITY native_handle_type native_handle() { return __base::unlock_shared(); }
203};
204#endif
205
206
207class _LIBCPP_TYPE_VIS _LIBCPP_AVAILABILITY_SHARED_MUTEX shared_timed_mutex
208{
209    __shared_mutex_base __base_;
210public:
211    shared_timed_mutex();
212    _LIBCPP_INLINE_VISIBILITY ~shared_timed_mutex() = default;
213
214    shared_timed_mutex(const shared_timed_mutex&) = delete;
215    shared_timed_mutex& operator=(const shared_timed_mutex&) = delete;
216
217    // Exclusive ownership
218    void lock();
219    bool try_lock();
220    template <class _Rep, class _Period>
221        _LIBCPP_INLINE_VISIBILITY
222        bool
223        try_lock_for(const chrono::duration<_Rep, _Period>& __rel_time)
224        {
225            return try_lock_until(chrono::steady_clock::now() + __rel_time);
226        }
227    template <class _Clock, class _Duration>
228        _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS
229        bool
230        try_lock_until(const chrono::time_point<_Clock, _Duration>& __abs_time);
231    void unlock();
232
233    // Shared ownership
234    void lock_shared();
235    bool try_lock_shared();
236    template <class _Rep, class _Period>
237        _LIBCPP_INLINE_VISIBILITY
238        bool
239        try_lock_shared_for(const chrono::duration<_Rep, _Period>& __rel_time)
240        {
241            return try_lock_shared_until(chrono::steady_clock::now() + __rel_time);
242        }
243    template <class _Clock, class _Duration>
244        _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS
245        bool
246        try_lock_shared_until(const chrono::time_point<_Clock, _Duration>& __abs_time);
247    void unlock_shared();
248};
249
250template <class _Clock, class _Duration>
251bool
252shared_timed_mutex::try_lock_until(
253                        const chrono::time_point<_Clock, _Duration>& __abs_time)
254{
255    unique_lock<mutex> __lk(__base_.__mut_);
256    if (__base_.__state_ & __base_.__write_entered_)
257    {
258        while (true)
259        {
260            cv_status __status = __base_.__gate1_.wait_until(__lk, __abs_time);
261            if ((__base_.__state_ & __base_.__write_entered_) == 0)
262                break;
263            if (__status == cv_status::timeout)
264                return false;
265        }
266    }
267    __base_.__state_ |= __base_.__write_entered_;
268    if (__base_.__state_ & __base_.__n_readers_)
269    {
270        while (true)
271        {
272            cv_status __status = __base_.__gate2_.wait_until(__lk, __abs_time);
273            if ((__base_.__state_ & __base_.__n_readers_) == 0)
274                break;
275            if (__status == cv_status::timeout)
276            {
277                __base_.__state_ &= ~__base_.__write_entered_;
278                __base_.__gate1_.notify_all();
279                return false;
280            }
281        }
282    }
283    return true;
284}
285
286template <class _Clock, class _Duration>
287bool
288shared_timed_mutex::try_lock_shared_until(
289                        const chrono::time_point<_Clock, _Duration>& __abs_time)
290{
291    unique_lock<mutex> __lk(__base_.__mut_);
292    if ((__base_.__state_ & __base_.__write_entered_) || (__base_.__state_ & __base_.__n_readers_) == __base_.__n_readers_)
293    {
294        while (true)
295        {
296            cv_status status = __base_.__gate1_.wait_until(__lk, __abs_time);
297            if ((__base_.__state_ & __base_.__write_entered_) == 0 &&
298                                       (__base_.__state_ & __base_.__n_readers_) < __base_.__n_readers_)
299                break;
300            if (status == cv_status::timeout)
301                return false;
302        }
303    }
304    unsigned __num_readers = (__base_.__state_ & __base_.__n_readers_) + 1;
305    __base_.__state_ &= ~__base_.__n_readers_;
306    __base_.__state_ |= __num_readers;
307    return true;
308}
309
310template <class _Mutex>
311class shared_lock
312{
313public:
314    typedef _Mutex mutex_type;
315
316private:
317    mutex_type* __m_;
318    bool __owns_;
319
320public:
321    _LIBCPP_INLINE_VISIBILITY
322    shared_lock() _NOEXCEPT
323        : __m_(nullptr),
324          __owns_(false)
325        {}
326
327    _LIBCPP_INLINE_VISIBILITY
328    explicit shared_lock(mutex_type& __m)
329        : __m_(_VSTD::addressof(__m)),
330          __owns_(true)
331        {__m_->lock_shared();}
332
333    _LIBCPP_INLINE_VISIBILITY
334    shared_lock(mutex_type& __m, defer_lock_t) _NOEXCEPT
335        : __m_(_VSTD::addressof(__m)),
336          __owns_(false)
337        {}
338
339    _LIBCPP_INLINE_VISIBILITY
340    shared_lock(mutex_type& __m, try_to_lock_t)
341        : __m_(_VSTD::addressof(__m)),
342          __owns_(__m.try_lock_shared())
343        {}
344
345    _LIBCPP_INLINE_VISIBILITY
346    shared_lock(mutex_type& __m, adopt_lock_t)
347        : __m_(_VSTD::addressof(__m)),
348          __owns_(true)
349        {}
350
351    template <class _Clock, class _Duration>
352        _LIBCPP_INLINE_VISIBILITY
353        shared_lock(mutex_type& __m,
354                    const chrono::time_point<_Clock, _Duration>& __abs_time)
355            : __m_(_VSTD::addressof(__m)),
356              __owns_(__m.try_lock_shared_until(__abs_time))
357            {}
358
359    template <class _Rep, class _Period>
360        _LIBCPP_INLINE_VISIBILITY
361        shared_lock(mutex_type& __m,
362                    const chrono::duration<_Rep, _Period>& __rel_time)
363            : __m_(_VSTD::addressof(__m)),
364              __owns_(__m.try_lock_shared_for(__rel_time))
365            {}
366
367    _LIBCPP_INLINE_VISIBILITY
368    ~shared_lock()
369    {
370        if (__owns_)
371            __m_->unlock_shared();
372    }
373
374    shared_lock(shared_lock const&) = delete;
375    shared_lock& operator=(shared_lock const&) = delete;
376
377    _LIBCPP_INLINE_VISIBILITY
378    shared_lock(shared_lock&& __u) _NOEXCEPT
379        : __m_(__u.__m_),
380          __owns_(__u.__owns_)
381        {
382            __u.__m_ = nullptr;
383            __u.__owns_ = false;
384        }
385
386    _LIBCPP_INLINE_VISIBILITY
387    shared_lock& operator=(shared_lock&& __u) _NOEXCEPT
388    {
389        if (__owns_)
390            __m_->unlock_shared();
391        __m_ = nullptr;
392        __owns_ = false;
393        __m_ = __u.__m_;
394        __owns_ = __u.__owns_;
395        __u.__m_ = nullptr;
396        __u.__owns_ = false;
397        return *this;
398    }
399
400    void lock();
401    bool try_lock();
402    template <class Rep, class Period>
403        bool try_lock_for(const chrono::duration<Rep, Period>& __rel_time);
404    template <class Clock, class Duration>
405        bool try_lock_until(const chrono::time_point<Clock, Duration>& __abs_time);
406    void unlock();
407
408    // Setters
409    _LIBCPP_INLINE_VISIBILITY
410    void swap(shared_lock& __u) _NOEXCEPT
411    {
412        _VSTD::swap(__m_, __u.__m_);
413        _VSTD::swap(__owns_, __u.__owns_);
414    }
415
416    _LIBCPP_INLINE_VISIBILITY
417    mutex_type* release() _NOEXCEPT
418    {
419        mutex_type* __m = __m_;
420        __m_ = nullptr;
421        __owns_ = false;
422        return __m;
423    }
424
425    // Getters
426    _LIBCPP_INLINE_VISIBILITY
427    bool owns_lock() const _NOEXCEPT {return __owns_;}
428
429    _LIBCPP_INLINE_VISIBILITY
430    explicit operator bool () const _NOEXCEPT {return __owns_;}
431
432    _LIBCPP_INLINE_VISIBILITY
433    mutex_type* mutex() const _NOEXCEPT {return __m_;}
434};
435_LIBCPP_CTAD_SUPPORTED_FOR_TYPE(shared_lock);
436
437template <class _Mutex>
438void
439shared_lock<_Mutex>::lock()
440{
441    if (__m_ == nullptr)
442        __throw_system_error(EPERM, "shared_lock::lock: references null mutex");
443    if (__owns_)
444        __throw_system_error(EDEADLK, "shared_lock::lock: already locked");
445    __m_->lock_shared();
446    __owns_ = true;
447}
448
449template <class _Mutex>
450bool
451shared_lock<_Mutex>::try_lock()
452{
453    if (__m_ == nullptr)
454        __throw_system_error(EPERM, "shared_lock::try_lock: references null mutex");
455    if (__owns_)
456        __throw_system_error(EDEADLK, "shared_lock::try_lock: already locked");
457    __owns_ = __m_->try_lock_shared();
458    return __owns_;
459}
460
461template <class _Mutex>
462template <class _Rep, class _Period>
463bool
464shared_lock<_Mutex>::try_lock_for(const chrono::duration<_Rep, _Period>& __d)
465{
466    if (__m_ == nullptr)
467        __throw_system_error(EPERM, "shared_lock::try_lock_for: references null mutex");
468    if (__owns_)
469        __throw_system_error(EDEADLK, "shared_lock::try_lock_for: already locked");
470    __owns_ = __m_->try_lock_shared_for(__d);
471    return __owns_;
472}
473
474template <class _Mutex>
475template <class _Clock, class _Duration>
476bool
477shared_lock<_Mutex>::try_lock_until(const chrono::time_point<_Clock, _Duration>& __t)
478{
479    if (__m_ == nullptr)
480        __throw_system_error(EPERM, "shared_lock::try_lock_until: references null mutex");
481    if (__owns_)
482        __throw_system_error(EDEADLK, "shared_lock::try_lock_until: already locked");
483    __owns_ = __m_->try_lock_shared_until(__t);
484    return __owns_;
485}
486
487template <class _Mutex>
488void
489shared_lock<_Mutex>::unlock()
490{
491    if (!__owns_)
492        __throw_system_error(EPERM, "shared_lock::unlock: not locked");
493    __m_->unlock_shared();
494    __owns_ = false;
495}
496
497template <class _Mutex>
498inline _LIBCPP_INLINE_VISIBILITY
499void
500swap(shared_lock<_Mutex>& __x, shared_lock<_Mutex>& __y) _NOEXCEPT
501    {__x.swap(__y);}
502
503_LIBCPP_END_NAMESPACE_STD
504
505#endif // _LIBCPP_STD_VER > 11
506
507_LIBCPP_POP_MACROS
508
509#endif // _LIBCPP_SHARED_MUTEX
510