• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //////////////////////////////////////////////////////////////////////////////
2 //  Code based on Howard Hinnant's shared_mutex class
3 //
4 // (C) Copyright Howard Hinnant 2007-2010. Distributed under the Boost
5 // Software License, Version 1.0. (see http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // (C) Copyright Ion Gaztanaga 2005-2012. Distributed under the Boost
8 // Software License, Version 1.0. (See accompanying file
9 // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
10 //
11 // See http://www.boost.org/libs/interprocess for documentation.
12 //
13 //////////////////////////////////////////////////////////////////////////////
14 
15 #ifndef BOOST_INTERPROCESS_SHARABLE_MUTEX_HPP
16 #define BOOST_INTERPROCESS_SHARABLE_MUTEX_HPP
17 
18 #ifndef BOOST_CONFIG_HPP
19 #  include <boost/config.hpp>
20 #endif
21 #
22 #if defined(BOOST_HAS_PRAGMA_ONCE)
23 #  pragma once
24 #endif
25 
26 #include <boost/interprocess/detail/config_begin.hpp>
27 #include <boost/interprocess/detail/workaround.hpp>
28 #include <boost/interprocess/sync/scoped_lock.hpp>
29 #include <boost/interprocess/detail/posix_time_types_wrk.hpp>
30 #include <boost/interprocess/sync/interprocess_mutex.hpp>
31 #include <boost/interprocess/sync/interprocess_condition.hpp>
32 #include <climits>
33 
34 
35 //!\file
36 //!Describes interprocess_sharable_mutex class
37 
38 namespace boost {
39 namespace interprocess {
40 
41 //!Wraps a interprocess_sharable_mutex that can be placed in shared memory and can be
42 //!shared between processes. Allows timed lock tries
43 class interprocess_sharable_mutex
44 {
45    //Non-copyable
46    interprocess_sharable_mutex(const interprocess_sharable_mutex &);
47    interprocess_sharable_mutex &operator=(const interprocess_sharable_mutex &);
48 
49    friend class interprocess_condition;
50    public:
51 
52    //!Constructs the sharable lock.
53    //!Throws interprocess_exception on error.
54    interprocess_sharable_mutex();
55 
56    //!Destroys the sharable lock.
57    //!Does not throw.
58    ~interprocess_sharable_mutex();
59 
60    //Exclusive locking
61 
62    //!Effects: The calling thread tries to obtain exclusive ownership of the mutex,
63    //!   and if another thread has exclusive or sharable ownership of
64    //!   the mutex, it waits until it can obtain the ownership.
65    //!Throws: interprocess_exception on error.
66    void lock();
67 
68    //!Effects: The calling thread tries to acquire exclusive ownership of the mutex
69    //!   without waiting. If no other thread has exclusive or sharable
70    //!   ownership of the mutex this succeeds.
71    //!Returns: If it can acquire exclusive ownership immediately returns true.
72    //!   If it has to wait, returns false.
73    //!Throws: interprocess_exception on error.
74    bool try_lock();
75 
76    //!Effects: The calling thread tries to acquire exclusive ownership of the mutex
77    //!   waiting if necessary until no other thread has exclusive or sharable
78    //!   ownership of the mutex or abs_time is reached.
79    //!Returns: If acquires exclusive ownership, returns true. Otherwise returns false.
80    //!Throws: interprocess_exception on error.
81    bool timed_lock(const boost::posix_time::ptime &abs_time);
82 
83    //!Precondition: The thread must have exclusive ownership of the mutex.
84    //!Effects: The calling thread releases the exclusive ownership of the mutex.
85    //!Throws: An exception derived from interprocess_exception on error.
86    void unlock();
87 
88    //Sharable locking
89 
90    //!Effects: The calling thread tries to obtain sharable ownership of the mutex,
91    //!   and if another thread has exclusive ownership of the mutex,
92    //!   waits until it can obtain the ownership.
93    //!Throws: interprocess_exception on error.
94    void lock_sharable();
95 
96    //!Effects: The calling thread tries to acquire sharable ownership of the mutex
97    //!   without waiting. If no other thread has exclusive ownership
98    //!   of the mutex this succeeds.
99    //!Returns: If it can acquire sharable ownership immediately returns true. If it
100    //!   has to wait, returns false.
101    //!Throws: interprocess_exception on error.
102    bool try_lock_sharable();
103 
104    //!Effects: The calling thread tries to acquire sharable ownership of the mutex
105    //!   waiting if necessary until no other thread has exclusive
106    //!   ownership of the mutex or abs_time is reached.
107    //!Returns: If acquires sharable ownership, returns true. Otherwise returns false.
108    //!Throws: interprocess_exception on error.
109    bool timed_lock_sharable(const boost::posix_time::ptime &abs_time);
110 
111    //!Precondition: The thread must have sharable ownership of the mutex.
112    //!Effects: The calling thread releases the sharable ownership of the mutex.
113    //!Throws: An exception derived from interprocess_exception on error.
114    void unlock_sharable();
115 
116    #if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED)
117    private:
118    typedef scoped_lock<interprocess_mutex> scoped_lock_t;
119 
120    //Pack all the control data in a word to be able
121    //to use atomic instructions in the future
122    struct control_word_t
123    {
124       unsigned exclusive_in   : 1;
125       unsigned num_shared     : sizeof(unsigned)*CHAR_BIT-1;
126    }                       m_ctrl;
127 
128    interprocess_mutex      m_mut;
129    interprocess_condition  m_first_gate;
130    interprocess_condition  m_second_gate;
131 
132    private:
133    //Rollback structures for exceptions or failure return values
134    struct exclusive_rollback
135    {
exclusive_rollbackboost::interprocess::interprocess_sharable_mutex::exclusive_rollback136       exclusive_rollback(control_word_t         &ctrl
137                         ,interprocess_condition &first_gate)
138          :  mp_ctrl(&ctrl), m_first_gate(first_gate)
139       {}
140 
releaseboost::interprocess::interprocess_sharable_mutex::exclusive_rollback141       void release()
142       {  mp_ctrl = 0;   }
143 
~exclusive_rollbackboost::interprocess::interprocess_sharable_mutex::exclusive_rollback144       ~exclusive_rollback()
145       {
146          if(mp_ctrl){
147             mp_ctrl->exclusive_in = 0;
148             m_first_gate.notify_all();
149          }
150       }
151       control_word_t          *mp_ctrl;
152       interprocess_condition  &m_first_gate;
153    };
154 
155    template<int Dummy>
156    struct base_constants_t
157    {
158       static const unsigned max_readers
159          = ~(unsigned(1) << (sizeof(unsigned)*CHAR_BIT-1));
160    };
161    typedef base_constants_t<0> constants;
162    #endif   //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED
163 };
164 
165 #if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED)
166 
167 template <int Dummy>
168 const unsigned interprocess_sharable_mutex::base_constants_t<Dummy>::max_readers;
169 
interprocess_sharable_mutex()170 inline interprocess_sharable_mutex::interprocess_sharable_mutex()
171 {
172    this->m_ctrl.exclusive_in  = 0;
173    this->m_ctrl.num_shared   = 0;
174 }
175 
~interprocess_sharable_mutex()176 inline interprocess_sharable_mutex::~interprocess_sharable_mutex()
177 {}
178 
lock()179 inline void interprocess_sharable_mutex::lock()
180 {
181    scoped_lock_t lck(m_mut);
182 
183    //The exclusive lock must block in the first gate
184    //if an exclusive lock has been acquired
185    while (this->m_ctrl.exclusive_in){
186       this->m_first_gate.wait(lck);
187    }
188 
189    //Mark that exclusive lock has been acquired
190    this->m_ctrl.exclusive_in = 1;
191 
192    //Prepare rollback
193    exclusive_rollback rollback(this->m_ctrl, this->m_first_gate);
194 
195    //Now wait until all readers are gone
196    while (this->m_ctrl.num_shared){
197       this->m_second_gate.wait(lck);
198    }
199    rollback.release();
200 }
201 
try_lock()202 inline bool interprocess_sharable_mutex::try_lock()
203 {
204    scoped_lock_t lck(m_mut, try_to_lock);
205 
206    //If we can't lock or any has there is any exclusive
207    //or sharable mark return false;
208    if(!lck.owns()
209       || this->m_ctrl.exclusive_in
210       || this->m_ctrl.num_shared){
211       return false;
212    }
213    this->m_ctrl.exclusive_in = 1;
214    return true;
215 }
216 
timed_lock(const boost::posix_time::ptime & abs_time)217 inline bool interprocess_sharable_mutex::timed_lock
218    (const boost::posix_time::ptime &abs_time)
219 {
220    scoped_lock_t lck(m_mut, abs_time);
221    if(!lck.owns())   return false;
222 
223    //The exclusive lock must block in the first gate
224    //if an exclusive lock has been acquired
225    while (this->m_ctrl.exclusive_in){
226       //Mutexes and condvars handle just fine infinite abs_times
227       //so avoid checking it here
228       if(!this->m_first_gate.timed_wait(lck, abs_time)){
229          if(this->m_ctrl.exclusive_in){
230             return false;
231          }
232          break;
233       }
234    }
235 
236    //Mark that exclusive lock has been acquired
237    this->m_ctrl.exclusive_in = 1;
238 
239    //Prepare rollback
240    exclusive_rollback rollback(this->m_ctrl, this->m_first_gate);
241 
242    //Now wait until all readers are gone
243    while (this->m_ctrl.num_shared){
244       //Mutexes and condvars handle just fine infinite abs_times
245       //so avoid checking it here
246       if(!this->m_second_gate.timed_wait(lck, abs_time)){
247          if(this->m_ctrl.num_shared){
248             return false;
249          }
250          break;
251       }
252    }
253    rollback.release();
254    return true;
255 }
256 
unlock()257 inline void interprocess_sharable_mutex::unlock()
258 {
259    scoped_lock_t lck(m_mut);
260    this->m_ctrl.exclusive_in = 0;
261    this->m_first_gate.notify_all();
262 }
263 
264 //Sharable locking
265 
lock_sharable()266 inline void interprocess_sharable_mutex::lock_sharable()
267 {
268    scoped_lock_t lck(m_mut);
269 
270    //The sharable lock must block in the first gate
271    //if an exclusive lock has been acquired
272    //or there are too many sharable locks
273    while(this->m_ctrl.exclusive_in
274         || this->m_ctrl.num_shared == constants::max_readers){
275       this->m_first_gate.wait(lck);
276    }
277 
278    //Increment sharable count
279    ++this->m_ctrl.num_shared;
280 }
281 
try_lock_sharable()282 inline bool interprocess_sharable_mutex::try_lock_sharable()
283 {
284    scoped_lock_t lck(m_mut, try_to_lock);
285 
286    //The sharable lock must fail
287    //if an exclusive lock has been acquired
288    //or there are too many sharable locks
289    if(!lck.owns()
290       || this->m_ctrl.exclusive_in
291       || this->m_ctrl.num_shared == constants::max_readers){
292       return false;
293    }
294 
295    //Increment sharable count
296    ++this->m_ctrl.num_shared;
297    return true;
298 }
299 
timed_lock_sharable(const boost::posix_time::ptime & abs_time)300 inline bool interprocess_sharable_mutex::timed_lock_sharable
301    (const boost::posix_time::ptime &abs_time)
302 {
303    scoped_lock_t lck(m_mut, abs_time);
304    if(!lck.owns())   return false;
305 
306    //The sharable lock must block in the first gate
307    //if an exclusive lock has been acquired
308    //or there are too many sharable locks
309    while (this->m_ctrl.exclusive_in
310          || this->m_ctrl.num_shared == constants::max_readers){
311       //Mutexes and condvars handle just fine infinite abs_times
312       //so avoid checking it here
313       if(!this->m_first_gate.timed_wait(lck, abs_time)){
314          if(this->m_ctrl.exclusive_in
315                || this->m_ctrl.num_shared == constants::max_readers){
316             return false;
317          }
318          break;
319       }
320    }
321 
322    //Increment sharable count
323    ++this->m_ctrl.num_shared;
324    return true;
325 }
326 
unlock_sharable()327 inline void interprocess_sharable_mutex::unlock_sharable()
328 {
329    scoped_lock_t lck(m_mut);
330    //Decrement sharable count
331    --this->m_ctrl.num_shared;
332    if (this->m_ctrl.num_shared == 0){
333       this->m_second_gate.notify_one();
334    }
335    //Check if there are blocked sharables because of
336    //there were too many sharables
337    else if(this->m_ctrl.num_shared == (constants::max_readers-1)){
338       this->m_first_gate.notify_all();
339    }
340 }
341 
342 #endif   //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED
343 
344 }  //namespace interprocess {
345 }  //namespace boost {
346 
347 #include <boost/interprocess/detail/config_end.hpp>
348 
349 #endif   //BOOST_INTERPROCESS_SHARABLE_MUTEX_HPP
350