xref: /freebsd/contrib/llvm-project/libcxx/include/shared_mutex (revision 5def4c47d4bd90b209b9b4a4ba9faec15846d8fd)
1// -*- C++ -*-
2//===------------------------ shared_mutex --------------------------------===//
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 <__config>
126#include <__availability>
127#include <version>
128
129_LIBCPP_PUSH_MACROS
130#include <__undef_macros>
131
132
133#if _LIBCPP_STD_VER > 11 || defined(_LIBCPP_BUILDING_LIBRARY)
134
135#include <__mutex_base>
136
137#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
138#pragma GCC system_header
139#endif
140
141#ifdef _LIBCPP_HAS_NO_THREADS
142#error <shared_mutex> is not supported on this single threaded system
143#else // !_LIBCPP_HAS_NO_THREADS
144
145_LIBCPP_BEGIN_NAMESPACE_STD
146
147struct _LIBCPP_TYPE_VIS _LIBCPP_AVAILABILITY_SHARED_MUTEX _LIBCPP_THREAD_SAFETY_ANNOTATION(capability("shared_mutex"))
148__shared_mutex_base
149{
150    mutex               __mut_;
151    condition_variable  __gate1_;
152    condition_variable  __gate2_;
153    unsigned            __state_;
154
155    static const unsigned __write_entered_ = 1U << (sizeof(unsigned)*__CHAR_BIT__ - 1);
156    static const unsigned __n_readers_ = ~__write_entered_;
157
158    __shared_mutex_base();
159    _LIBCPP_INLINE_VISIBILITY ~__shared_mutex_base() = default;
160
161    __shared_mutex_base(const __shared_mutex_base&) = delete;
162    __shared_mutex_base& operator=(const __shared_mutex_base&) = delete;
163
164    // Exclusive ownership
165    void lock() _LIBCPP_THREAD_SAFETY_ANNOTATION(acquire_capability()); // blocking
166    bool try_lock() _LIBCPP_THREAD_SAFETY_ANNOTATION(try_acquire_capability(true));
167    void unlock() _LIBCPP_THREAD_SAFETY_ANNOTATION(release_capability());
168
169    // Shared ownership
170    void lock_shared() _LIBCPP_THREAD_SAFETY_ANNOTATION(acquire_shared_capability()); // blocking
171    bool try_lock_shared() _LIBCPP_THREAD_SAFETY_ANNOTATION(try_acquire_shared_capability(true));
172    void unlock_shared() _LIBCPP_THREAD_SAFETY_ANNOTATION(release_shared_capability());
173
174//     typedef implementation-defined native_handle_type; // See 30.2.3
175//     native_handle_type native_handle(); // See 30.2.3
176};
177
178
179#if _LIBCPP_STD_VER > 14
180class _LIBCPP_TYPE_VIS _LIBCPP_AVAILABILITY_SHARED_MUTEX shared_mutex
181{
182    __shared_mutex_base __base;
183public:
184    _LIBCPP_INLINE_VISIBILITY shared_mutex() : __base() {}
185    _LIBCPP_INLINE_VISIBILITY ~shared_mutex() = default;
186
187    shared_mutex(const shared_mutex&) = delete;
188    shared_mutex& operator=(const shared_mutex&) = delete;
189
190    // Exclusive ownership
191    _LIBCPP_INLINE_VISIBILITY void lock()     { return __base.lock(); }
192    _LIBCPP_INLINE_VISIBILITY bool try_lock() { return __base.try_lock(); }
193    _LIBCPP_INLINE_VISIBILITY void unlock()   { return __base.unlock(); }
194
195    // Shared ownership
196    _LIBCPP_INLINE_VISIBILITY void lock_shared()     { return __base.lock_shared(); }
197    _LIBCPP_INLINE_VISIBILITY bool try_lock_shared() { return __base.try_lock_shared(); }
198    _LIBCPP_INLINE_VISIBILITY void unlock_shared()   { return __base.unlock_shared(); }
199
200//     typedef __shared_mutex_base::native_handle_type native_handle_type;
201//     _LIBCPP_INLINE_VISIBILITY native_handle_type native_handle() { return __base::unlock_shared(); }
202};
203#endif
204
205
206class _LIBCPP_TYPE_VIS _LIBCPP_AVAILABILITY_SHARED_MUTEX shared_timed_mutex
207{
208    __shared_mutex_base __base;
209public:
210    shared_timed_mutex();
211    _LIBCPP_INLINE_VISIBILITY ~shared_timed_mutex() = default;
212
213    shared_timed_mutex(const shared_timed_mutex&) = delete;
214    shared_timed_mutex& operator=(const shared_timed_mutex&) = delete;
215
216    // Exclusive ownership
217    void lock();
218    bool try_lock();
219    template <class _Rep, class _Period>
220        _LIBCPP_INLINE_VISIBILITY
221        bool
222        try_lock_for(const chrono::duration<_Rep, _Period>& __rel_time)
223        {
224            return try_lock_until(chrono::steady_clock::now() + __rel_time);
225        }
226    template <class _Clock, class _Duration>
227        _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS
228        bool
229        try_lock_until(const chrono::time_point<_Clock, _Duration>& __abs_time);
230    void unlock();
231
232    // Shared ownership
233    void lock_shared();
234    bool try_lock_shared();
235    template <class _Rep, class _Period>
236        _LIBCPP_INLINE_VISIBILITY
237        bool
238        try_lock_shared_for(const chrono::duration<_Rep, _Period>& __rel_time)
239        {
240            return try_lock_shared_until(chrono::steady_clock::now() + __rel_time);
241        }
242    template <class _Clock, class _Duration>
243        _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS
244        bool
245        try_lock_shared_until(const chrono::time_point<_Clock, _Duration>& __abs_time);
246    void unlock_shared();
247};
248
249template <class _Clock, class _Duration>
250bool
251shared_timed_mutex::try_lock_until(
252                        const chrono::time_point<_Clock, _Duration>& __abs_time)
253{
254    unique_lock<mutex> __lk(__base.__mut_);
255    if (__base.__state_ & __base.__write_entered_)
256    {
257        while (true)
258        {
259            cv_status __status = __base.__gate1_.wait_until(__lk, __abs_time);
260            if ((__base.__state_ & __base.__write_entered_) == 0)
261                break;
262            if (__status == cv_status::timeout)
263                return false;
264        }
265    }
266    __base.__state_ |= __base.__write_entered_;
267    if (__base.__state_ & __base.__n_readers_)
268    {
269        while (true)
270        {
271            cv_status __status = __base.__gate2_.wait_until(__lk, __abs_time);
272            if ((__base.__state_ & __base.__n_readers_) == 0)
273                break;
274            if (__status == cv_status::timeout)
275            {
276                __base.__state_ &= ~__base.__write_entered_;
277                __base.__gate1_.notify_all();
278                return false;
279            }
280        }
281    }
282    return true;
283}
284
285template <class _Clock, class _Duration>
286bool
287shared_timed_mutex::try_lock_shared_until(
288                        const chrono::time_point<_Clock, _Duration>& __abs_time)
289{
290    unique_lock<mutex> __lk(__base.__mut_);
291    if ((__base.__state_ & __base.__write_entered_) || (__base.__state_ & __base.__n_readers_) == __base.__n_readers_)
292    {
293        while (true)
294        {
295            cv_status status = __base.__gate1_.wait_until(__lk, __abs_time);
296            if ((__base.__state_ & __base.__write_entered_) == 0 &&
297                                       (__base.__state_ & __base.__n_readers_) < __base.__n_readers_)
298                break;
299            if (status == cv_status::timeout)
300                return false;
301        }
302    }
303    unsigned __num_readers = (__base.__state_ & __base.__n_readers_) + 1;
304    __base.__state_ &= ~__base.__n_readers_;
305    __base.__state_ |= __num_readers;
306    return true;
307}
308
309template <class _Mutex>
310class shared_lock
311{
312public:
313    typedef _Mutex mutex_type;
314
315private:
316    mutex_type* __m_;
317    bool __owns_;
318
319public:
320    _LIBCPP_INLINE_VISIBILITY
321    shared_lock() _NOEXCEPT
322        : __m_(nullptr),
323          __owns_(false)
324        {}
325
326    _LIBCPP_INLINE_VISIBILITY
327    explicit shared_lock(mutex_type& __m)
328        : __m_(_VSTD::addressof(__m)),
329          __owns_(true)
330        {__m_->lock_shared();}
331
332    _LIBCPP_INLINE_VISIBILITY
333    shared_lock(mutex_type& __m, defer_lock_t) _NOEXCEPT
334        : __m_(_VSTD::addressof(__m)),
335          __owns_(false)
336        {}
337
338    _LIBCPP_INLINE_VISIBILITY
339    shared_lock(mutex_type& __m, try_to_lock_t)
340        : __m_(_VSTD::addressof(__m)),
341          __owns_(__m.try_lock_shared())
342        {}
343
344    _LIBCPP_INLINE_VISIBILITY
345    shared_lock(mutex_type& __m, adopt_lock_t)
346        : __m_(_VSTD::addressof(__m)),
347          __owns_(true)
348        {}
349
350    template <class _Clock, class _Duration>
351        _LIBCPP_INLINE_VISIBILITY
352        shared_lock(mutex_type& __m,
353                    const chrono::time_point<_Clock, _Duration>& __abs_time)
354            : __m_(_VSTD::addressof(__m)),
355              __owns_(__m.try_lock_shared_until(__abs_time))
356            {}
357
358    template <class _Rep, class _Period>
359        _LIBCPP_INLINE_VISIBILITY
360        shared_lock(mutex_type& __m,
361                    const chrono::duration<_Rep, _Period>& __rel_time)
362            : __m_(_VSTD::addressof(__m)),
363              __owns_(__m.try_lock_shared_for(__rel_time))
364            {}
365
366    _LIBCPP_INLINE_VISIBILITY
367    ~shared_lock()
368    {
369        if (__owns_)
370            __m_->unlock_shared();
371    }
372
373    shared_lock(shared_lock const&) = delete;
374    shared_lock& operator=(shared_lock const&) = delete;
375
376    _LIBCPP_INLINE_VISIBILITY
377    shared_lock(shared_lock&& __u) _NOEXCEPT
378        : __m_(__u.__m_),
379          __owns_(__u.__owns_)
380        {
381            __u.__m_ = nullptr;
382            __u.__owns_ = false;
383        }
384
385    _LIBCPP_INLINE_VISIBILITY
386    shared_lock& operator=(shared_lock&& __u) _NOEXCEPT
387    {
388        if (__owns_)
389            __m_->unlock_shared();
390        __m_ = nullptr;
391        __owns_ = false;
392        __m_ = __u.__m_;
393        __owns_ = __u.__owns_;
394        __u.__m_ = nullptr;
395        __u.__owns_ = false;
396        return *this;
397    }
398
399    void lock();
400    bool try_lock();
401    template <class Rep, class Period>
402        bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);
403    template <class Clock, class Duration>
404        bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);
405    void unlock();
406
407    // Setters
408    _LIBCPP_INLINE_VISIBILITY
409    void swap(shared_lock& __u) _NOEXCEPT
410    {
411        _VSTD::swap(__m_, __u.__m_);
412        _VSTD::swap(__owns_, __u.__owns_);
413    }
414
415    _LIBCPP_INLINE_VISIBILITY
416    mutex_type* release() _NOEXCEPT
417    {
418        mutex_type* __m = __m_;
419        __m_ = nullptr;
420        __owns_ = false;
421        return __m;
422    }
423
424    // Getters
425    _LIBCPP_INLINE_VISIBILITY
426    bool owns_lock() const _NOEXCEPT {return __owns_;}
427
428    _LIBCPP_INLINE_VISIBILITY
429    explicit operator bool () const _NOEXCEPT {return __owns_;}
430
431    _LIBCPP_INLINE_VISIBILITY
432    mutex_type* mutex() const _NOEXCEPT {return __m_;}
433};
434
435template <class _Mutex>
436void
437shared_lock<_Mutex>::lock()
438{
439    if (__m_ == nullptr)
440        __throw_system_error(EPERM, "shared_lock::lock: references null mutex");
441    if (__owns_)
442        __throw_system_error(EDEADLK, "shared_lock::lock: already locked");
443    __m_->lock_shared();
444    __owns_ = true;
445}
446
447template <class _Mutex>
448bool
449shared_lock<_Mutex>::try_lock()
450{
451    if (__m_ == nullptr)
452        __throw_system_error(EPERM, "shared_lock::try_lock: references null mutex");
453    if (__owns_)
454        __throw_system_error(EDEADLK, "shared_lock::try_lock: already locked");
455    __owns_ = __m_->try_lock_shared();
456    return __owns_;
457}
458
459template <class _Mutex>
460template <class _Rep, class _Period>
461bool
462shared_lock<_Mutex>::try_lock_for(const chrono::duration<_Rep, _Period>& __d)
463{
464    if (__m_ == nullptr)
465        __throw_system_error(EPERM, "shared_lock::try_lock_for: references null mutex");
466    if (__owns_)
467        __throw_system_error(EDEADLK, "shared_lock::try_lock_for: already locked");
468    __owns_ = __m_->try_lock_shared_for(__d);
469    return __owns_;
470}
471
472template <class _Mutex>
473template <class _Clock, class _Duration>
474bool
475shared_lock<_Mutex>::try_lock_until(const chrono::time_point<_Clock, _Duration>& __t)
476{
477    if (__m_ == nullptr)
478        __throw_system_error(EPERM, "shared_lock::try_lock_until: references null mutex");
479    if (__owns_)
480        __throw_system_error(EDEADLK, "shared_lock::try_lock_until: already locked");
481    __owns_ = __m_->try_lock_shared_until(__t);
482    return __owns_;
483}
484
485template <class _Mutex>
486void
487shared_lock<_Mutex>::unlock()
488{
489    if (!__owns_)
490        __throw_system_error(EPERM, "shared_lock::unlock: not locked");
491    __m_->unlock_shared();
492    __owns_ = false;
493}
494
495template <class _Mutex>
496inline _LIBCPP_INLINE_VISIBILITY
497void
498swap(shared_lock<_Mutex>& __x, shared_lock<_Mutex>& __y) _NOEXCEPT
499    {__x.swap(__y);}
500
501_LIBCPP_END_NAMESPACE_STD
502
503#endif  // !_LIBCPP_HAS_NO_THREADS
504
505#endif  // _LIBCPP_STD_VER > 11
506
507_LIBCPP_POP_MACROS
508
509#endif  // _LIBCPP_SHARED_MUTEX
510