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