PR c++/64359
[official-gcc.git] / libstdc++-v3 / include / std / shared_mutex
blobc193eb2d90e90ce96639fbc4c656df1046156dba
1 // <shared_mutex> -*- C++ -*-
3 // Copyright (C) 2013-2014 Free Software Foundation, Inc.
4 //
5 // This file is part of the GNU ISO C++ Library.  This library is free
6 // software; you can redistribute it and/or modify it under the
7 // terms of the GNU General Public License as published by the
8 // Free Software Foundation; either version 3, or (at your option)
9 // any later version.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 // GNU General Public License for more details.
16 // Under Section 7 of GPL version 3, you are granted additional
17 // permissions described in the GCC Runtime Library Exception, version
18 // 3.1, as published by the Free Software Foundation.
20 // You should have received a copy of the GNU General Public License and
21 // a copy of the GCC Runtime Library Exception along with this program;
22 // see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
23 // <http://www.gnu.org/licenses/>.
25 /** @file include/shared_mutex
26  *  This is a Standard C++ Library header.
27  */
29 #ifndef _GLIBCXX_SHARED_MUTEX
30 #define _GLIBCXX_SHARED_MUTEX 1
32 #pragma GCC system_header
34 #if __cplusplus <= 201103L
35 # include <bits/c++14_warning.h>
36 #else
38 #include <bits/c++config.h>
39 #include <mutex>
40 #include <condition_variable>
41 #include <bits/functexcept.h>
43 namespace std _GLIBCXX_VISIBILITY(default)
45 _GLIBCXX_BEGIN_NAMESPACE_VERSION
47   /**
48    * @ingroup mutexes
49    * @{
50    */
52 #ifdef _GLIBCXX_USE_C99_STDINT_TR1
53 #ifdef _GLIBCXX_HAS_GTHREADS
55 #define __cpp_lib_shared_timed_mutex 201402
57   /// shared_timed_mutex
58   class shared_timed_mutex
59   {
60 #if _GTHREAD_USE_MUTEX_TIMEDLOCK
61     struct _Mutex : mutex, __timed_mutex_impl<_Mutex>
62     {
63       template<typename _Rep, typename _Period>
64         bool
65         try_lock_for(const chrono::duration<_Rep, _Period>& __rtime)
66         { return _M_try_lock_for(__rtime); }
68       template<typename _Clock, typename _Duration>
69         bool
70         try_lock_until(const chrono::time_point<_Clock, _Duration>& __atime)
71         { return _M_try_lock_until(__atime); }
72     };
73 #else
74     typedef mutex _Mutex;
75 #endif
77     // Based on Howard Hinnant's reference implementation from N2406
79     _Mutex              _M_mut;
80     condition_variable  _M_gate1;
81     condition_variable  _M_gate2;
82     unsigned            _M_state;
84     static constexpr unsigned _S_write_entered
85       = 1U << (sizeof(unsigned)*__CHAR_BIT__ - 1);
86     static constexpr unsigned _M_n_readers = ~_S_write_entered;
88   public:
89     shared_timed_mutex() : _M_state(0) {}
91     ~shared_timed_mutex()
92     {
93       _GLIBCXX_DEBUG_ASSERT( _M_state == 0 );
94     }
96     shared_timed_mutex(const shared_timed_mutex&) = delete;
97     shared_timed_mutex& operator=(const shared_timed_mutex&) = delete;
99     // Exclusive ownership
101     void
102     lock()
103     {
104       unique_lock<mutex> __lk(_M_mut);
105       while (_M_state & _S_write_entered)
106         _M_gate1.wait(__lk);
107       _M_state |= _S_write_entered;
108       while (_M_state & _M_n_readers)
109         _M_gate2.wait(__lk);
110     }
112     bool
113     try_lock()
114     {
115       unique_lock<mutex> __lk(_M_mut, try_to_lock);
116       if (__lk.owns_lock() && _M_state == 0)
117         {
118           _M_state = _S_write_entered;
119           return true;
120         }
121       return false;
122     }
124 #if _GTHREAD_USE_MUTEX_TIMEDLOCK
125     template<typename _Rep, typename _Period>
126       bool
127       try_lock_for(const chrono::duration<_Rep, _Period>& __rel_time)
128       {
129         unique_lock<_Mutex> __lk(_M_mut, __rel_time);
130         if (__lk.owns_lock() && _M_state == 0)
131           {
132             _M_state = _S_write_entered;
133             return true;
134           }
135         return false;
136       }
138     template<typename _Clock, typename _Duration>
139       bool
140       try_lock_until(const chrono::time_point<_Clock, _Duration>& __abs_time)
141       {
142         unique_lock<_Mutex> __lk(_M_mut, __abs_time);
143         if (__lk.owns_lock() && _M_state == 0)
144           {
145             _M_state = _S_write_entered;
146             return true;
147           }
148         return false;
149       }
150 #endif
152     void
153     unlock()
154     {
155       {
156         lock_guard<_Mutex> __lk(_M_mut);
157         _M_state = 0;
158       }
159       _M_gate1.notify_all();
160     }
162     // Shared ownership
164     void
165     lock_shared()
166     {
167       unique_lock<mutex> __lk(_M_mut);
168       while ((_M_state & _S_write_entered)
169           || (_M_state & _M_n_readers) == _M_n_readers)
170         {
171           _M_gate1.wait(__lk);
172         }
173       unsigned __num_readers = (_M_state & _M_n_readers) + 1;
174       _M_state &= ~_M_n_readers;
175       _M_state |= __num_readers;
176     }
178     bool
179     try_lock_shared()
180     {
181       unique_lock<_Mutex> __lk(_M_mut, try_to_lock);
182       unsigned __num_readers = _M_state & _M_n_readers;
183       if (__lk.owns_lock() && !(_M_state & _S_write_entered)
184           && __num_readers != _M_n_readers)
185         {
186           ++__num_readers;
187           _M_state &= ~_M_n_readers;
188           _M_state |= __num_readers;
189           return true;
190         }
191       return false;
192     }
194 #if _GTHREAD_USE_MUTEX_TIMEDLOCK
195     template<typename _Rep, typename _Period>
196       bool
197       try_lock_shared_for(const chrono::duration<_Rep, _Period>& __rel_time)
198       {
199         unique_lock<_Mutex> __lk(_M_mut, __rel_time);
200         if (__lk.owns_lock())
201           {
202             unsigned __num_readers = _M_state & _M_n_readers;
203             if (!(_M_state & _S_write_entered)
204                 && __num_readers != _M_n_readers)
205               {
206                 ++__num_readers;
207                 _M_state &= ~_M_n_readers;
208                 _M_state |= __num_readers;
209                 return true;
210               }
211           }
212         return false;
213       }
215     template <typename _Clock, typename _Duration>
216       bool
217       try_lock_shared_until(const chrono::time_point<_Clock,
218                                                      _Duration>& __abs_time)
219       {
220         unique_lock<_Mutex> __lk(_M_mut, __abs_time);
221         if (__lk.owns_lock())
222           {
223             unsigned __num_readers = _M_state & _M_n_readers;
224             if (!(_M_state & _S_write_entered)
225                 && __num_readers != _M_n_readers)
226               {
227                 ++__num_readers;
228                 _M_state &= ~_M_n_readers;
229                 _M_state |= __num_readers;
230                 return true;
231               }
232           }
233         return false;
234       }
235 #endif
237     void
238     unlock_shared()
239     {
240       lock_guard<_Mutex> __lk(_M_mut);
241       unsigned __num_readers = (_M_state & _M_n_readers) - 1;
242       _M_state &= ~_M_n_readers;
243       _M_state |= __num_readers;
244       if (_M_state & _S_write_entered)
245         {
246           if (__num_readers == 0)
247             _M_gate2.notify_one();
248         }
249       else
250         {
251           if (__num_readers == _M_n_readers - 1)
252             _M_gate1.notify_one();
253         }
254     }
255   };
256 #endif // _GLIBCXX_HAS_GTHREADS
258   /// shared_lock
259   template<typename _Mutex>
260     class shared_lock
261     {
262     public:
263       typedef _Mutex mutex_type;
265       // Shared locking
267       shared_lock() noexcept : _M_pm(nullptr), _M_owns(false) { }
269       explicit
270       shared_lock(mutex_type& __m) : _M_pm(&__m), _M_owns(true)
271       { __m.lock_shared(); }
273       shared_lock(mutex_type& __m, defer_lock_t) noexcept
274       : _M_pm(&__m), _M_owns(false) { }
276       shared_lock(mutex_type& __m, try_to_lock_t)
277       : _M_pm(&__m), _M_owns(__m.try_lock_shared()) { }
279       shared_lock(mutex_type& __m, adopt_lock_t)
280       : _M_pm(&__m), _M_owns(true) { }
282       template<typename _Clock, typename _Duration>
283         shared_lock(mutex_type& __m,
284                     const chrono::time_point<_Clock, _Duration>& __abs_time)
285       : _M_pm(&__m), _M_owns(__m.try_lock_shared_until(__abs_time)) { }
287       template<typename _Rep, typename _Period>
288         shared_lock(mutex_type& __m,
289                     const chrono::duration<_Rep, _Period>& __rel_time)
290       : _M_pm(&__m), _M_owns(__m.try_lock_shared_for(__rel_time)) { }
292       ~shared_lock()
293       {
294         if (_M_owns)
295           _M_pm->unlock_shared();
296       }
298       shared_lock(shared_lock const&) = delete;
299       shared_lock& operator=(shared_lock const&) = delete;
301       shared_lock(shared_lock&& __sl) noexcept : shared_lock()
302       { swap(__sl); }
304       shared_lock&
305       operator=(shared_lock&& __sl) noexcept
306       {
307         shared_lock(std::move(__sl)).swap(*this);
308         return *this;
309       }
311       void
312       lock()
313       {
314         _M_lockable();
315         _M_pm->lock_shared();
316         _M_owns = true;
317       }
319       bool
320       try_lock()
321       {
322         _M_lockable();
323         return _M_owns = _M_pm->try_lock_shared();
324       }
326       template<typename _Rep, typename _Period>
327         bool
328         try_lock_for(const chrono::duration<_Rep, _Period>& __rel_time)
329         {
330           _M_lockable();
331           return _M_owns = _M_pm->try_lock_shared_for(__rel_time);
332         }
334       template<typename _Clock, typename _Duration>
335         bool
336         try_lock_until(const chrono::time_point<_Clock, _Duration>& __abs_time)
337         {
338           _M_lockable();
339           return _M_owns = _M_pm->try_lock_shared_until(__abs_time);
340         }
342       void
343       unlock()
344       {
345         if (!_M_owns)
346           __throw_system_error(int(errc::resource_deadlock_would_occur));
347         _M_pm->unlock_shared();
348         _M_owns = false;
349       }
351       // Setters
353       void
354       swap(shared_lock& __u) noexcept
355       {
356         std::swap(_M_pm, __u._M_pm);
357         std::swap(_M_owns, __u._M_owns);
358       }
360       mutex_type*
361       release() noexcept
362       {
363         _M_owns = false;
364         return std::exchange(_M_pm, nullptr);
365       }
367       // Getters
369       bool owns_lock() const noexcept { return _M_owns; }
371       explicit operator bool() const noexcept { return _M_owns; }
373       mutex_type* mutex() const noexcept { return _M_pm; }
375     private:
376       void
377       _M_lockable() const
378       {
379         if (_M_pm == nullptr)
380           __throw_system_error(int(errc::operation_not_permitted));
381         if (_M_owns)
382           __throw_system_error(int(errc::resource_deadlock_would_occur));
383       }
385       mutex_type*       _M_pm;
386       bool              _M_owns;
387     };
389   /// Swap specialization for shared_lock
390   template<typename _Mutex>
391     void
392     swap(shared_lock<_Mutex>& __x, shared_lock<_Mutex>& __y) noexcept
393     { __x.swap(__y); }
395 #endif // _GLIBCXX_USE_C99_STDINT_TR1
397   // @} group mutexes
398 _GLIBCXX_END_NAMESPACE_VERSION
399 } // namespace
401 #endif // C++14
403 #endif // _GLIBCXX_SHARED_MUTEX