1 /*
2  * Distributed under the Boost Software License, Version 1.0.
3  *    (See accompanying file LICENSE_1_0.txt or copy at
4  *          http://www.boost.org/LICENSE_1_0.txt)
5  *
6  * (C) Copyright 2007 Anthony Williams
7  * (C) Copyright 2011-2012 Vicente J. Botet Escriba
8  * (C) Copyright 2013 Andrey Semashev
9  */
10 /*!
11  * \file   locks/shared_lock.hpp
12  *
13  * \brief  This header defines a shared lock guard.
14  */
15 
16 #ifndef BOOST_SYNC_LOCKS_SHARED_LOCK_HPP_INCLUDED_
17 #define BOOST_SYNC_LOCKS_SHARED_LOCK_HPP_INCLUDED_
18 
19 #include <cstddef>
20 #include <boost/move/core.hpp>
21 #include <boost/move/utility.hpp>
22 #include <boost/utility/enable_if.hpp>
23 #include <boost/utility/explicit_operator_bool.hpp>
24 #include <boost/sync/detail/config.hpp>
25 #include <boost/sync/detail/time_traits.hpp>
26 #include <boost/sync/detail/throw_exception.hpp>
27 #include <boost/sync/detail/system_error.hpp>
28 #include <boost/sync/exceptions/lock_error.hpp>
29 #include <boost/sync/locks/lock_options.hpp>
30 #include <boost/sync/locks/shared_lock_fwd.hpp>
31 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
32 #include <boost/sync/locks/unique_lock_fwd.hpp>
33 #include <boost/sync/locks/upgrade_lock_fwd.hpp>
34 #else
35 // These locks need to be defined for Boost.Move emulation
36 #include <boost/sync/locks/unique_lock.hpp>
37 #include <boost/sync/locks/upgrade_lock.hpp>
38 #endif
39 
40 #include <boost/sync/detail/header.hpp>
41 
42 #ifdef BOOST_HAS_PRAGMA_ONCE
43 #pragma once
44 #endif
45 
46 namespace boost {
47 
48 namespace sync {
49 
50 /*!
51  * \brief A shared lock scope guard
52  */
53 template< typename Mutex >
54 class shared_lock
55 {
56 public:
57     typedef Mutex mutex_type;
58 
59 private:
60     mutex_type* m_mutex;
61     bool m_is_locked;
62 
63 public:
64     BOOST_MOVABLE_BUT_NOT_COPYABLE(shared_lock)
65 
66 public:
shared_lock()67     shared_lock() BOOST_NOEXCEPT :
68         m_mutex(NULL), m_is_locked(false)
69     {
70     }
71 
shared_lock(mutex_type & m)72     explicit shared_lock(mutex_type& m) :
73         m_mutex(&m), m_is_locked(false)
74     {
75         lock();
76     }
shared_lock(mutex_type & m,adopt_lock_t)77     shared_lock(mutex_type& m, adopt_lock_t) BOOST_NOEXCEPT :
78         m_mutex(&m), m_is_locked(true)
79     {
80     }
shared_lock(mutex_type & m,defer_lock_t)81     shared_lock(mutex_type& m, defer_lock_t) BOOST_NOEXCEPT :
82         m_mutex(&m), m_is_locked(false)
83     {
84     }
shared_lock(mutex_type & m,try_to_lock_t)85     shared_lock(mutex_type& m, try_to_lock_t) :
86         m_mutex(&m), m_is_locked(false)
87     {
88         try_lock();
89     }
90 
91     template< typename Time >
shared_lock(typename enable_if_c<detail::time_traits<Time>::is_specialized,mutex_type &>::type m,Time const & t)92     shared_lock(typename enable_if_c< detail::time_traits< Time >::is_specialized, mutex_type& >::type m, Time const& t) :
93         m_mutex(&m), m_is_locked(false)
94     {
95         timed_lock(t);
96     }
97 
shared_lock(BOOST_RV_REF (shared_lock)that)98     shared_lock(BOOST_RV_REF(shared_lock) that) BOOST_NOEXCEPT :
99         m_mutex(that.m_mutex), m_is_locked(that.m_is_locked)
100     {
101         that.m_mutex = NULL;
102         that.m_is_locked = false;
103     }
104 
105     // Conversion from unique locking
shared_lock(BOOST_RV_REF (unique_lock<mutex_type>)that)106     explicit shared_lock(BOOST_RV_REF(unique_lock< mutex_type >) that) :
107         m_mutex(that.m_mutex), m_is_locked(that.m_is_locked)
108     {
109         if (m_is_locked)
110             m_mutex->unlock_and_lock_shared();
111         that.release();
112     }
113 
shared_lock(BOOST_RV_REF (upgrade_lock<mutex_type>)that)114     explicit shared_lock(BOOST_RV_REF(upgrade_lock< mutex_type >) that) :
115         m_mutex(that.m_mutex), m_is_locked(that.m_is_locked)
116     {
117         if (m_is_locked)
118             m_mutex->unlock_upgrade_and_lock_shared();
119         that.release();
120     }
121 
~shared_lock()122     ~shared_lock()
123     {
124         if (m_is_locked)
125             m_mutex->unlock_shared();
126     }
127 
operator =(BOOST_RV_REF (shared_lock)that)128     shared_lock& operator= (BOOST_RV_REF(shared_lock) that) BOOST_NOEXCEPT
129     {
130         swap((shared_lock&)that);
131         return *this;
132     }
133 
operator =(BOOST_RV_REF (unique_lock<mutex_type>)that)134     shared_lock& operator= (BOOST_RV_REF(unique_lock< mutex_type >) that)
135     {
136         shared_lock temp(boost::move(that));
137         swap(temp);
138         return *this;
139     }
140 
operator =(BOOST_RV_REF (upgrade_lock<mutex_type>)that)141     shared_lock& operator= (BOOST_RV_REF(upgrade_lock< mutex_type >) that)
142     {
143         shared_lock temp(boost::move(that));
144         swap(temp);
145         return *this;
146     }
147 
lock()148     void lock()
149     {
150         if (!m_mutex)
151             BOOST_SYNC_DETAIL_THROW(lock_error, (detail::system_ns::errc::operation_not_permitted)("boost shared_lock has no mutex"));
152 
153         if (m_is_locked)
154             BOOST_SYNC_DETAIL_THROW(lock_error, (detail::system_ns::errc::resource_deadlock_would_occur)("boost shared_lock already owns the mutex"));
155 
156         m_mutex->lock_shared();
157         m_is_locked = true;
158     }
159 
try_lock()160     bool try_lock()
161     {
162         if (!m_mutex)
163             BOOST_SYNC_DETAIL_THROW(lock_error, (detail::system_ns::errc::operation_not_permitted)("boost shared_lock has no mutex"));
164 
165         if (m_is_locked)
166             BOOST_SYNC_DETAIL_THROW(lock_error, (detail::system_ns::errc::resource_deadlock_would_occur)("boost shared_lock already owns the mutex"));
167 
168         m_is_locked = m_mutex->try_lock_shared();
169 
170         return m_is_locked;
171     }
172 
173     template< typename Time >
timed_lock(Time const & time)174     typename enable_if_c< detail::time_traits< Time >::is_specialized, bool >::type timed_lock(Time const& time)
175     {
176         if (!m_mutex)
177             BOOST_SYNC_DETAIL_THROW(lock_error, (detail::system_ns::errc::operation_not_permitted)("boost shared_lock has no mutex"));
178 
179         if (m_is_locked)
180             BOOST_SYNC_DETAIL_THROW(lock_error, (detail::system_ns::errc::resource_deadlock_would_occur)("boost shared_lock already owns the mutex"));
181 
182         m_is_locked = m_mutex->timed_lock_shared(time);
183 
184         return m_is_locked;
185     }
186 
187     template< typename Duration >
try_lock_for(Duration const & rel_time)188     typename detail::enable_if_tag< Duration, detail::time_duration_tag, bool >::type try_lock_for(Duration const& rel_time)
189     {
190         if (!m_mutex)
191             BOOST_SYNC_DETAIL_THROW(lock_error, (detail::system_ns::errc::operation_not_permitted)("boost shared_lock has no mutex"));
192 
193         if (m_is_locked)
194             BOOST_SYNC_DETAIL_THROW(lock_error, (detail::system_ns::errc::resource_deadlock_would_occur)("boost shared_lock already owns the mutex"));
195 
196         m_is_locked = m_mutex->try_lock_shared_for(rel_time);
197 
198         return m_is_locked;
199     }
200 
201     template< typename TimePoint >
try_lock_until(TimePoint const & abs_time)202     typename detail::enable_if_tag< TimePoint, detail::time_point_tag, bool >::type try_lock_until(TimePoint const& abs_time)
203     {
204         if (!m_mutex)
205             BOOST_SYNC_DETAIL_THROW(lock_error, (detail::system_ns::errc::operation_not_permitted)("boost shared_lock has no mutex"));
206 
207         if (m_is_locked)
208             BOOST_SYNC_DETAIL_THROW(lock_error, (detail::system_ns::errc::resource_deadlock_would_occur)("boost shared_lock already owns the mutex"));
209 
210         m_is_locked = m_mutex->try_lock_shared_until(abs_time);
211 
212         return m_is_locked;
213     }
214 
unlock()215     void unlock()
216     {
217         if (!m_mutex)
218             BOOST_SYNC_DETAIL_THROW(lock_error, (detail::system_ns::errc::operation_not_permitted)("boost shared_lock has no mutex"));
219 
220         if (!m_is_locked)
221             BOOST_SYNC_DETAIL_THROW(lock_error, (detail::system_ns::errc::operation_not_permitted)("boost shared_lock doesn't own the mutex"));
222 
223         m_mutex->unlock_shared();
224         m_is_locked = false;
225     }
226 
227     BOOST_EXPLICIT_OPERATOR_BOOL()
228 
229     bool operator! () const BOOST_NOEXCEPT
230     {
231         return !m_is_locked;
232     }
233 
owns_lock() const234     bool owns_lock() const BOOST_NOEXCEPT
235     {
236         return m_is_locked;
237     }
238 
mutex() const239     mutex_type* mutex() const BOOST_NOEXCEPT
240     {
241         return m_mutex;
242     }
243 
release()244     mutex_type* release() BOOST_NOEXCEPT
245     {
246         mutex_type* const res = m_mutex;
247         m_mutex = NULL;
248         m_is_locked = false;
249         return res;
250     }
251 
swap(shared_lock & that)252     void swap(shared_lock& that) BOOST_NOEXCEPT
253     {
254         mutex_type* const p = m_mutex;
255         m_mutex = that.m_mutex;
256         that.m_mutex = p;
257         const bool f = m_is_locked;
258         m_is_locked = that.m_is_locked;
259         that.m_is_locked = f;
260     }
261 };
262 
263 template< typename Mutex >
swap(shared_lock<Mutex> & lhs,shared_lock<Mutex> & rhs)264 inline void swap(shared_lock< Mutex >& lhs, shared_lock< Mutex >& rhs) BOOST_NOEXCEPT
265 {
266     lhs.swap(rhs);
267 }
268 
269 } // namespace sync
270 
271 } // namespace boost
272 
273 #include <boost/sync/detail/footer.hpp>
274 
275 #endif // BOOST_SYNC_LOCKS_SHARED_LOCK_HPP_INCLUDED_
276