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/sync/interprocess_mutex.hpp>
30 #include <boost/interprocess/sync/interprocess_condition.hpp>
31 #include <climits>
32 
33 
34 //!\file
35 //!Describes interprocess_sharable_mutex class
36 
37 namespace boost {
38 namespace interprocess {
39 
40 //!Wraps a interprocess_sharable_mutex that can be placed in shared memory and can be
41 //!shared between processes. Allows timed lock tries
42 class interprocess_sharable_mutex
43 {
44    //Non-copyable
45    interprocess_sharable_mutex(const interprocess_sharable_mutex &);
46    interprocess_sharable_mutex &operator=(const interprocess_sharable_mutex &);
47 
48    friend class interprocess_condition;
49    public:
50 
51    //!Constructs the sharable lock.
52    //!Throws interprocess_exception on error.
53    interprocess_sharable_mutex();
54 
55    //!Destroys the sharable lock.
56    //!Does not throw.
57    ~interprocess_sharable_mutex();
58 
59    //Exclusive locking
60 
61    //!Requires: The calling thread does not own the mutex.
62    //!
63    //!Effects: The calling thread tries to obtain exclusive ownership of the mutex,
64    //!   and if another thread has exclusive or sharable ownership of
65    //!   the mutex, it waits until it can obtain the ownership.
66    //!Throws: interprocess_exception on error.
67    //!
68    //!Note: A program may deadlock if the thread that has ownership calls
69    //!   this function. If the implementation can detect the deadlock,
70    //!   an exception could be thrown.
71    void lock();
72 
73    //!Requires: The calling thread does not own the mutex.
74    //!
75    //!Effects: The calling thread tries to acquire exclusive ownership of the mutex
76    //!   without waiting. If no other thread has exclusive or sharable
77    //!   ownership of the mutex this succeeds.
78    //!Returns: If it can acquire exclusive ownership immediately returns true.
79    //!   If it has to wait, returns false.
80    //!Throws: interprocess_exception on error.
81    //!
82    //!Note: A program may deadlock if the thread that has ownership calls
83    //!   this function. If the implementation can detect the deadlock,
84    //!   an exception could be thrown.
85    bool try_lock();
86 
87    //!Requires: The calling thread does not own the mutex.
88    //!
89    //!Effects: The calling thread tries to acquire exclusive ownership of the mutex
90    //!   waiting if necessary until no other thread has exclusive or sharable
91    //!   ownership of the mutex or abs_time is reached.
92    //!Returns: If acquires exclusive ownership, returns true. Otherwise returns false.
93    //!Throws: interprocess_exception on error.
94    //!
95    //!Note: A program may deadlock if the thread that has ownership calls
96    //!   this function. If the implementation can detect the deadlock,
97    //!   an exception could be thrown.
98    template<class TimePoint>
99    bool timed_lock(const TimePoint &abs_time);
100 
101    //!Same as `timed_lock`, but this function is modeled after the
102    //!standard library interface.
try_lock_until(const TimePoint & abs_time)103    template<class TimePoint> bool try_lock_until(const TimePoint &abs_time)
104    {  return this->timed_lock(abs_time);  }
105 
106    //!Same as `timed_lock`, but this function is modeled after the
107    //!standard library interface.
try_lock_for(const Duration & dur)108    template<class Duration>  bool try_lock_for(const Duration &dur)
109    {  return this->timed_lock(ipcdetail::duration_to_ustime(dur)); }
110 
111    //!Precondition: The thread must have exclusive ownership of the mutex.
112    //!Effects: The calling thread releases the exclusive ownership of the mutex.
113    //!Throws: An exception derived from interprocess_exception on error.
114    void unlock();
115 
116    //Sharable locking
117 
118    //!Requires: The calling thread does not own the mutex.
119    //!
120    //!Effects: The calling thread tries to obtain sharable ownership of the mutex,
121    //!   and if another thread has exclusive ownership of the mutex,
122    //!   waits until it can obtain the ownership.
123    //!Throws: interprocess_exception on error.
124    //!
125    //!Note: A program may deadlock if the thread that has ownership calls
126    //!   this function. If the implementation can detect the deadlock,
127    //!   an exception could be thrown.
128    void lock_sharable();
129 
130    //!Same as `lock_sharable` but with a std-compatible interface
131    //!
lock_shared()132    void lock_shared()
133    {  this->lock_sharable();  }
134 
135    //!Requires: The calling thread does not own the mutex.
136    //!
137    //!Effects: The calling thread tries to acquire sharable ownership of the mutex
138    //!   without waiting. If no other thread has exclusive ownership
139    //!   of the mutex this succeeds.
140    //!Returns: If it can acquire sharable ownership immediately returns true. If it
141    //!   has to wait, returns false.
142    //!Throws: interprocess_exception on error.
143    //!
144    //!Note: A program may deadlock if the thread that has ownership calls
145    //!   this function. If the implementation can detect the deadlock,
146    //!   an exception could be thrown.
147    bool try_lock_sharable();
148 
149    //!Same as `try_lock_sharable` but with a std-compatible interface
150    //!
try_lock_shared()151    bool try_lock_shared()
152    {  return this->try_lock_sharable();  }
153 
154    //!Requires: The calling thread does not own the mutex.
155    //!
156    //!Effects: The calling thread tries to acquire sharable ownership of the mutex
157    //!   waiting if necessary until no other thread has exclusive
158    //!   ownership of the mutex or abs_time is reached.
159    //!Returns: If acquires sharable ownership, returns true. Otherwise returns false.
160    //!Throws: interprocess_exception on error.
161    //!
162    //!Note: A program may deadlock if the thread that has ownership calls
163    //!   this function. If the implementation can detect the deadlock,
164    //!   an exception could be thrown.
165    template<class TimePoint>
166    bool timed_lock_sharable(const TimePoint &abs_time);
167 
168    //!Same as `timed_lock_sharable`, but this function is modeled after the
169    //!standard library interface.
try_lock_shared_until(const TimePoint & abs_time)170    template<class TimePoint> bool try_lock_shared_until(const TimePoint &abs_time)
171    {  return this->timed_lock_sharable(abs_time);  }
172 
173    //!Same as `timed_lock_sharable`, but this function is modeled after the
174    //!standard library interface.
try_lock_shared_for(const Duration & dur)175    template<class Duration>  bool try_lock_shared_for(const Duration &dur)
176    {  return this->timed_lock_sharable(ipcdetail::duration_to_ustime(dur)); }
177 
178    //!Precondition: The thread must have sharable ownership of the mutex.
179    //!Effects: The calling thread releases the sharable ownership of the mutex.
180    //!Throws: An exception derived from interprocess_exception on error.
181    void unlock_sharable();
182 
183    //!Same as `unlock_sharable` but with a std-compatible interface
184    //!
unlock_shared()185    void unlock_shared()
186    {  this->unlock_sharable();  }
187 
188    #if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED)
189    private:
190    typedef scoped_lock<interprocess_mutex> scoped_lock_t;
191 
192    //Pack all the control data in a word to be able
193    //to use atomic instructions in the future
194    struct control_word_t
195    {
196       unsigned exclusive_in   : 1;
197       unsigned num_shared     : sizeof(unsigned)*CHAR_BIT-1;
198    }                       m_ctrl;
199 
200    interprocess_mutex      m_mut;
201    interprocess_condition  m_first_gate;
202    interprocess_condition  m_second_gate;
203 
204    private:
205    //Rollback structures for exceptions or failure return values
206    struct exclusive_rollback
207    {
exclusive_rollbackboost::interprocess::interprocess_sharable_mutex::exclusive_rollback208       exclusive_rollback(control_word_t         &ctrl
209                         ,interprocess_condition &first_gate)
210          :  mp_ctrl(&ctrl), m_first_gate(first_gate)
211       {}
212 
releaseboost::interprocess::interprocess_sharable_mutex::exclusive_rollback213       void release()
214       {  mp_ctrl = 0;   }
215 
~exclusive_rollbackboost::interprocess::interprocess_sharable_mutex::exclusive_rollback216       ~exclusive_rollback()
217       {
218          if(mp_ctrl){
219             mp_ctrl->exclusive_in = 0;
220             m_first_gate.notify_all();
221          }
222       }
223       control_word_t          *mp_ctrl;
224       interprocess_condition  &m_first_gate;
225    };
226 
227    template<int Dummy>
228    struct base_constants_t
229    {
230       static const unsigned max_readers
231          = ~(unsigned(1) << (sizeof(unsigned)*CHAR_BIT-1));
232    };
233    typedef base_constants_t<0> constants;
234    #endif   //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED
235 };
236 
237 #if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED)
238 
239 template <int Dummy>
240 const unsigned interprocess_sharable_mutex::base_constants_t<Dummy>::max_readers;
241 
interprocess_sharable_mutex()242 inline interprocess_sharable_mutex::interprocess_sharable_mutex()
243 {
244    this->m_ctrl.exclusive_in  = 0;
245    this->m_ctrl.num_shared   = 0;
246 }
247 
~interprocess_sharable_mutex()248 inline interprocess_sharable_mutex::~interprocess_sharable_mutex()
249 {}
250 
lock()251 inline void interprocess_sharable_mutex::lock()
252 {
253    scoped_lock_t lck(m_mut);
254 
255    //The exclusive lock must block in the first gate
256    //if an exclusive lock has been acquired
257    while (this->m_ctrl.exclusive_in){
258       this->m_first_gate.wait(lck);
259    }
260 
261    //Mark that exclusive lock has been acquired
262    this->m_ctrl.exclusive_in = 1;
263 
264    //Prepare rollback
265    exclusive_rollback rollback(this->m_ctrl, this->m_first_gate);
266 
267    //Now wait until all readers are gone
268    while (this->m_ctrl.num_shared){
269       this->m_second_gate.wait(lck);
270    }
271    rollback.release();
272 }
273 
try_lock()274 inline bool interprocess_sharable_mutex::try_lock()
275 {
276    scoped_lock_t lck(m_mut, try_to_lock);
277 
278    //If we can't lock or any has there is any exclusive
279    //or sharable mark return false;
280    if(!lck.owns()
281       || this->m_ctrl.exclusive_in
282       || this->m_ctrl.num_shared){
283       return false;
284    }
285    this->m_ctrl.exclusive_in = 1;
286    return true;
287 }
288 
289 template<class TimePoint>
timed_lock(const TimePoint & abs_time)290 inline bool interprocess_sharable_mutex::timed_lock
291    (const TimePoint &abs_time)
292 {
293    scoped_lock_t lck(m_mut, abs_time);
294    if(!lck.owns())   return false;
295 
296    //The exclusive lock must block in the first gate
297    //if an exclusive lock has been acquired
298    while (this->m_ctrl.exclusive_in){
299       //Mutexes and condvars handle just fine infinite abs_times
300       //so avoid checking it here
301       if(!this->m_first_gate.timed_wait(lck, abs_time)){
302          if(this->m_ctrl.exclusive_in){
303             return false;
304          }
305          break;
306       }
307    }
308 
309    //Mark that exclusive lock has been acquired
310    this->m_ctrl.exclusive_in = 1;
311 
312    //Prepare rollback
313    exclusive_rollback rollback(this->m_ctrl, this->m_first_gate);
314 
315    //Now wait until all readers are gone
316    while (this->m_ctrl.num_shared){
317       //Mutexes and condvars handle just fine infinite abs_times
318       //so avoid checking it here
319       if(!this->m_second_gate.timed_wait(lck, abs_time)){
320          if(this->m_ctrl.num_shared){
321             return false;
322          }
323          break;
324       }
325    }
326    rollback.release();
327    return true;
328 }
329 
unlock()330 inline void interprocess_sharable_mutex::unlock()
331 {
332    scoped_lock_t lck(m_mut);
333    this->m_ctrl.exclusive_in = 0;
334    this->m_first_gate.notify_all();
335 }
336 
337 //Sharable locking
338 
lock_sharable()339 inline void interprocess_sharable_mutex::lock_sharable()
340 {
341    scoped_lock_t lck(m_mut);
342 
343    //The sharable lock must block in the first gate
344    //if an exclusive lock has been acquired
345    //or there are too many sharable locks
346    while(this->m_ctrl.exclusive_in
347         || this->m_ctrl.num_shared == constants::max_readers){
348       this->m_first_gate.wait(lck);
349    }
350 
351    //Increment sharable count
352    ++this->m_ctrl.num_shared;
353 }
354 
try_lock_sharable()355 inline bool interprocess_sharable_mutex::try_lock_sharable()
356 {
357    scoped_lock_t lck(m_mut, try_to_lock);
358 
359    //The sharable lock must fail
360    //if an exclusive lock has been acquired
361    //or there are too many sharable locks
362    if(!lck.owns()
363       || this->m_ctrl.exclusive_in
364       || this->m_ctrl.num_shared == constants::max_readers){
365       return false;
366    }
367 
368    //Increment sharable count
369    ++this->m_ctrl.num_shared;
370    return true;
371 }
372 
373 template<class TimePoint>
timed_lock_sharable(const TimePoint & abs_time)374 inline bool interprocess_sharable_mutex::timed_lock_sharable
375    (const TimePoint &abs_time)
376 {
377    scoped_lock_t lck(m_mut, abs_time);
378    if(!lck.owns())   return false;
379 
380    //The sharable lock must block in the first gate
381    //if an exclusive lock has been acquired
382    //or there are too many sharable locks
383    while (this->m_ctrl.exclusive_in
384          || this->m_ctrl.num_shared == constants::max_readers){
385       //Mutexes and condvars handle just fine infinite abs_times
386       //so avoid checking it here
387       if(!this->m_first_gate.timed_wait(lck, abs_time)){
388          if(this->m_ctrl.exclusive_in
389                || this->m_ctrl.num_shared == constants::max_readers){
390             return false;
391          }
392          break;
393       }
394    }
395 
396    //Increment sharable count
397    ++this->m_ctrl.num_shared;
398    return true;
399 }
400 
unlock_sharable()401 inline void interprocess_sharable_mutex::unlock_sharable()
402 {
403    scoped_lock_t lck(m_mut);
404    //Decrement sharable count
405    --this->m_ctrl.num_shared;
406    if (this->m_ctrl.num_shared == 0){
407       this->m_second_gate.notify_one();
408    }
409    //Check if there are blocked sharables because of
410    //there were too many sharables
411    else if(this->m_ctrl.num_shared == (constants::max_readers-1)){
412       this->m_first_gate.notify_all();
413    }
414 }
415 
416 #endif   //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED
417 
418 }  //namespace interprocess {
419 }  //namespace boost {
420 
421 #include <boost/interprocess/detail/config_end.hpp>
422 
423 #endif   //BOOST_INTERPROCESS_SHARABLE_MUTEX_HPP
424