1 ////////////////////////////////////////////////////////////////////////////////
3 // Code based on Howard Hinnant's upgrade_mutex class
5 // (C) Copyright Ion Gaztanaga 2005-2012. Distributed under the Boost
6 // Software License, Version 1.0. (See accompanying file
7 // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
9 // See http://www.boost.org/libs/interprocess for documentation.
11 //////////////////////////////////////////////////////////////////////////////
13 #ifndef BOOST_INTERPROCESS_UPGRADABLE_MUTEX_HPP
14 #define BOOST_INTERPROCESS_UPGRADABLE_MUTEX_HPP
16 #ifndef BOOST_CONFIG_HPP
17 # include <boost/config.hpp>
20 #if defined(BOOST_HAS_PRAGMA_ONCE)
24 #include <boost/interprocess/detail/config_begin.hpp>
25 #include <boost/interprocess/detail/workaround.hpp>
26 #include <boost/interprocess/sync/scoped_lock.hpp>
27 #include <boost/interprocess/detail/posix_time_types_wrk.hpp>
28 #include <boost/interprocess/sync/interprocess_mutex.hpp>
29 #include <boost/interprocess/sync/interprocess_condition.hpp>
34 //!Describes interprocess_upgradable_mutex class
37 namespace interprocess {
39 //!Wraps a interprocess_upgradable_mutex that can be placed in shared memory and can be
40 //!shared between processes. Allows timed lock tries
41 class interprocess_upgradable_mutex
44 interprocess_upgradable_mutex(const interprocess_upgradable_mutex &);
45 interprocess_upgradable_mutex &operator=(const interprocess_upgradable_mutex &);
47 friend class interprocess_condition;
50 //!Constructs the upgradable lock.
51 //!Throws interprocess_exception on error.
52 interprocess_upgradable_mutex();
54 //!Destroys the upgradable lock.
56 ~interprocess_upgradable_mutex();
60 //!Effects: The calling thread tries to obtain exclusive ownership of the mutex,
61 //! and if another thread has exclusive, sharable or upgradable ownership of
62 //! the mutex, it waits until it can obtain the ownership.
63 //!Throws: interprocess_exception on error.
66 //!Effects: The calling thread tries to acquire exclusive ownership of the mutex
67 //! without waiting. If no other thread has exclusive, sharable or upgradable
68 //! ownership of the mutex this succeeds.
69 //!Returns: If it can acquire exclusive ownership immediately returns true.
70 //! If it has to wait, returns false.
71 //!Throws: interprocess_exception on error.
74 //!Effects: The calling thread tries to acquire exclusive ownership of the mutex
75 //! waiting if necessary until no other thread has exclusive, sharable or
76 //! upgradable ownership of the mutex or abs_time is reached.
77 //!Returns: If acquires exclusive ownership, returns true. Otherwise returns false.
78 //!Throws: interprocess_exception on error.
79 bool timed_lock(const boost::posix_time::ptime &abs_time);
81 //!Precondition: The thread must have exclusive ownership of the mutex.
82 //!Effects: The calling thread releases the exclusive ownership of the mutex.
83 //!Throws: An exception derived from interprocess_exception on error.
88 //!Effects: The calling thread tries to obtain sharable ownership of the mutex,
89 //! and if another thread has exclusive ownership of the mutex,
90 //! waits until it can obtain the ownership.
91 //!Throws: interprocess_exception on error.
94 //!Effects: The calling thread tries to acquire sharable ownership of the mutex
95 //! without waiting. If no other thread has exclusive ownership
96 //! of the mutex this succeeds.
97 //!Returns: If it can acquire sharable ownership immediately returns true. If it
98 //! has to wait, returns false.
99 //!Throws: interprocess_exception on error.
100 bool try_lock_sharable();
102 //!Effects: The calling thread tries to acquire sharable ownership of the mutex
103 //! waiting if necessary until no other thread has exclusive
104 //! ownership of the mutex or abs_time is reached.
105 //!Returns: If acquires sharable ownership, returns true. Otherwise returns false.
106 //!Throws: interprocess_exception on error.
107 bool timed_lock_sharable(const boost::posix_time::ptime &abs_time);
109 //!Precondition: The thread must have sharable ownership of the mutex.
110 //!Effects: The calling thread releases the sharable ownership of the mutex.
111 //!Throws: An exception derived from interprocess_exception on error.
112 void unlock_sharable();
116 //!Effects: The calling thread tries to obtain upgradable ownership of the mutex,
117 //! and if another thread has exclusive or upgradable ownership of the mutex,
118 //! waits until it can obtain the ownership.
119 //!Throws: interprocess_exception on error.
120 void lock_upgradable();
122 //!Effects: The calling thread tries to acquire upgradable ownership of the mutex
123 //! without waiting. If no other thread has exclusive or upgradable ownership
124 //! of the mutex this succeeds.
125 //!Returns: If it can acquire upgradable ownership immediately returns true.
126 //! If it has to wait, returns false.
127 //!Throws: interprocess_exception on error.
128 bool try_lock_upgradable();
130 //!Effects: The calling thread tries to acquire upgradable ownership of the mutex
131 //! waiting if necessary until no other thread has exclusive or upgradable
132 //! ownership of the mutex or abs_time is reached.
133 //!Returns: If acquires upgradable ownership, returns true. Otherwise returns false.
134 //!Throws: interprocess_exception on error.
135 bool timed_lock_upgradable(const boost::posix_time::ptime &abs_time);
137 //!Precondition: The thread must have upgradable ownership of the mutex.
138 //!Effects: The calling thread releases the upgradable ownership of the mutex.
139 //!Throws: An exception derived from interprocess_exception on error.
140 void unlock_upgradable();
144 //!Precondition: The thread must have exclusive ownership of the mutex.
145 //!Effects: The thread atomically releases exclusive ownership and acquires
146 //! upgradable ownership. This operation is non-blocking.
147 //!Throws: An exception derived from interprocess_exception on error.
148 void unlock_and_lock_upgradable();
150 //!Precondition: The thread must have exclusive ownership of the mutex.
151 //!Effects: The thread atomically releases exclusive ownership and acquires
152 //! sharable ownership. This operation is non-blocking.
153 //!Throws: An exception derived from interprocess_exception on error.
154 void unlock_and_lock_sharable();
156 //!Precondition: The thread must have upgradable ownership of the mutex.
157 //!Effects: The thread atomically releases upgradable ownership and acquires
158 //! sharable ownership. This operation is non-blocking.
159 //!Throws: An exception derived from interprocess_exception on error.
160 void unlock_upgradable_and_lock_sharable();
164 //!Precondition: The thread must have upgradable ownership of the mutex.
165 //!Effects: The thread atomically releases upgradable ownership and acquires
166 //! exclusive ownership. This operation will block until all threads with
167 //! sharable ownership release their sharable lock.
168 //!Throws: An exception derived from interprocess_exception on error.
169 void unlock_upgradable_and_lock();
171 //!Precondition: The thread must have upgradable ownership of the mutex.
172 //!Effects: The thread atomically releases upgradable ownership and tries to
173 //! acquire exclusive ownership. This operation will fail if there are threads
174 //! with sharable ownership, but it will maintain upgradable ownership.
175 //!Returns: If acquires exclusive ownership, returns true. Otherwise returns false.
176 //!Throws: An exception derived from interprocess_exception on error.
177 bool try_unlock_upgradable_and_lock();
179 //!Precondition: The thread must have upgradable ownership of the mutex.
180 //!Effects: The thread atomically releases upgradable ownership and tries to acquire
181 //! exclusive ownership, waiting if necessary until abs_time. This operation will
182 //! fail if there are threads with sharable ownership or timeout reaches, but it
183 //! will maintain upgradable ownership.
184 //!Returns: If acquires exclusive ownership, returns true. Otherwise returns false.
185 //!Throws: An exception derived from interprocess_exception on error. */
186 bool timed_unlock_upgradable_and_lock(const boost::posix_time::ptime &abs_time);
188 //!Precondition: The thread must have sharable ownership of the mutex.
189 //!Effects: The thread atomically releases sharable ownership and tries to acquire
190 //! exclusive ownership. This operation will fail if there are threads with sharable
191 //! or upgradable ownership, but it will maintain sharable ownership.
192 //!Returns: If acquires exclusive ownership, returns true. Otherwise returns false.
193 //!Throws: An exception derived from interprocess_exception on error.
194 bool try_unlock_sharable_and_lock();
196 //!Precondition: The thread must have sharable ownership of the mutex.
197 //!Effects: The thread atomically releases sharable ownership and tries to acquire
198 //! upgradable ownership. This operation will fail if there are threads with sharable
199 //! or upgradable ownership, but it will maintain sharable ownership.
200 //!Returns: If acquires upgradable ownership, returns true. Otherwise returns false.
201 //!Throws: An exception derived from interprocess_exception on error.
202 bool try_unlock_sharable_and_lock_upgradable();
204 #if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED)
206 typedef scoped_lock<interprocess_mutex> scoped_lock_t;
208 //Pack all the control data in a word to be able
209 //to use atomic instructions in the future
210 struct control_word_t
212 unsigned exclusive_in : 1;
213 unsigned upgradable_in : 1;
214 unsigned num_upr_shar : sizeof(unsigned)*CHAR_BIT-2;
217 interprocess_mutex m_mut;
218 interprocess_condition m_first_gate;
219 interprocess_condition m_second_gate;
222 //Rollback structures for exceptions or failure return values
223 struct exclusive_rollback
225 exclusive_rollback(control_word_t &ctrl
226 ,interprocess_condition &first_gate)
227 : mp_ctrl(&ctrl), m_first_gate(first_gate)
233 ~exclusive_rollback()
236 mp_ctrl->exclusive_in = 0;
237 m_first_gate.notify_all();
240 control_word_t *mp_ctrl;
241 interprocess_condition &m_first_gate;
244 struct upgradable_to_exclusive_rollback
246 upgradable_to_exclusive_rollback(control_word_t &ctrl)
253 ~upgradable_to_exclusive_rollback()
256 //Recover upgradable lock
257 mp_ctrl->upgradable_in = 1;
258 ++mp_ctrl->num_upr_shar;
259 //Execute the second half of exclusive locking
260 mp_ctrl->exclusive_in = 0;
263 control_word_t *mp_ctrl;
267 struct base_constants_t
269 static const unsigned max_readers
270 = ~(unsigned(3) << (sizeof(unsigned)*CHAR_BIT-2));
272 typedef base_constants_t<0> constants;
273 #endif //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED
276 #if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED)
279 const unsigned interprocess_upgradable_mutex::base_constants_t<Dummy>::max_readers;
281 inline interprocess_upgradable_mutex::interprocess_upgradable_mutex()
283 this->m_ctrl.exclusive_in = 0;
284 this->m_ctrl.upgradable_in = 0;
285 this->m_ctrl.num_upr_shar = 0;
288 inline interprocess_upgradable_mutex::~interprocess_upgradable_mutex()
291 inline void interprocess_upgradable_mutex::lock()
293 scoped_lock_t lck(m_mut);
295 //The exclusive lock must block in the first gate
296 //if an exclusive or upgradable lock has been acquired
297 while (this->m_ctrl.exclusive_in || this->m_ctrl.upgradable_in){
298 this->m_first_gate.wait(lck);
301 //Mark that exclusive lock has been acquired
302 this->m_ctrl.exclusive_in = 1;
305 exclusive_rollback rollback(this->m_ctrl, this->m_first_gate);
307 //Now wait until all readers are gone
308 while (this->m_ctrl.num_upr_shar){
309 this->m_second_gate.wait(lck);
314 inline bool interprocess_upgradable_mutex::try_lock()
316 scoped_lock_t lck(m_mut, try_to_lock);
318 //If we can't lock or any has there is any exclusive, upgradable
319 //or sharable mark return false;
321 || this->m_ctrl.exclusive_in
322 || this->m_ctrl.num_upr_shar){
325 this->m_ctrl.exclusive_in = 1;
329 inline bool interprocess_upgradable_mutex::timed_lock
330 (const boost::posix_time::ptime &abs_time)
332 //Mutexes and condvars handle just fine infinite abs_times
333 //so avoid checking it here
334 scoped_lock_t lck(m_mut, abs_time);
335 if(!lck.owns()) return false;
337 //The exclusive lock must block in the first gate
338 //if an exclusive or upgradable lock has been acquired
339 while (this->m_ctrl.exclusive_in || this->m_ctrl.upgradable_in){
340 if(!this->m_first_gate.timed_wait(lck, abs_time)){
341 if(this->m_ctrl.exclusive_in || this->m_ctrl.upgradable_in){
348 //Mark that exclusive lock has been acquired
349 this->m_ctrl.exclusive_in = 1;
352 exclusive_rollback rollback(this->m_ctrl, this->m_first_gate);
354 //Now wait until all readers are gone
355 while (this->m_ctrl.num_upr_shar){
356 if(!this->m_second_gate.timed_wait(lck, abs_time)){
357 if(this->m_ctrl.num_upr_shar){
367 inline void interprocess_upgradable_mutex::unlock()
369 scoped_lock_t lck(m_mut);
370 this->m_ctrl.exclusive_in = 0;
371 this->m_first_gate.notify_all();
376 inline void interprocess_upgradable_mutex::lock_upgradable()
378 scoped_lock_t lck(m_mut);
380 //The upgradable lock must block in the first gate
381 //if an exclusive or upgradable lock has been acquired
382 //or there are too many sharable locks
383 while(this->m_ctrl.exclusive_in || this->m_ctrl.upgradable_in
384 || this->m_ctrl.num_upr_shar == constants::max_readers){
385 this->m_first_gate.wait(lck);
388 //Mark that upgradable lock has been acquired
389 //And add upgradable to the sharable count
390 this->m_ctrl.upgradable_in = 1;
391 ++this->m_ctrl.num_upr_shar;
394 inline bool interprocess_upgradable_mutex::try_lock_upgradable()
396 scoped_lock_t lck(m_mut, try_to_lock);
398 //The upgradable lock must fail
399 //if an exclusive or upgradable lock has been acquired
400 //or there are too many sharable locks
402 || this->m_ctrl.exclusive_in
403 || this->m_ctrl.upgradable_in
404 || this->m_ctrl.num_upr_shar == constants::max_readers){
408 //Mark that upgradable lock has been acquired
409 //And add upgradable to the sharable count
410 this->m_ctrl.upgradable_in = 1;
411 ++this->m_ctrl.num_upr_shar;
415 inline bool interprocess_upgradable_mutex::timed_lock_upgradable
416 (const boost::posix_time::ptime &abs_time)
418 //Mutexes and condvars handle just fine infinite abs_times
419 //so avoid checking it here
420 scoped_lock_t lck(m_mut, abs_time);
421 if(!lck.owns()) return false;
423 //The upgradable lock must block in the first gate
424 //if an exclusive or upgradable lock has been acquired
425 //or there are too many sharable locks
426 while(this->m_ctrl.exclusive_in
427 || this->m_ctrl.upgradable_in
428 || this->m_ctrl.num_upr_shar == constants::max_readers){
429 if(!this->m_first_gate.timed_wait(lck, abs_time)){
430 if((this->m_ctrl.exclusive_in
431 || this->m_ctrl.upgradable_in
432 || this->m_ctrl.num_upr_shar == constants::max_readers)){
439 //Mark that upgradable lock has been acquired
440 //And add upgradable to the sharable count
441 this->m_ctrl.upgradable_in = 1;
442 ++this->m_ctrl.num_upr_shar;
446 inline void interprocess_upgradable_mutex::unlock_upgradable()
448 scoped_lock_t lck(m_mut);
449 //Mark that upgradable lock has been acquired
450 //And add upgradable to the sharable count
451 this->m_ctrl.upgradable_in = 0;
452 --this->m_ctrl.num_upr_shar;
453 this->m_first_gate.notify_all();
458 inline void interprocess_upgradable_mutex::lock_sharable()
460 scoped_lock_t lck(m_mut);
462 //The sharable lock must block in the first gate
463 //if an exclusive lock has been acquired
464 //or there are too many sharable locks
465 while(this->m_ctrl.exclusive_in
466 || this->m_ctrl.num_upr_shar == constants::max_readers){
467 this->m_first_gate.wait(lck);
470 //Increment sharable count
471 ++this->m_ctrl.num_upr_shar;
474 inline bool interprocess_upgradable_mutex::try_lock_sharable()
476 scoped_lock_t lck(m_mut, try_to_lock);
478 //The sharable lock must fail
479 //if an exclusive lock has been acquired
480 //or there are too many sharable locks
482 || this->m_ctrl.exclusive_in
483 || this->m_ctrl.num_upr_shar == constants::max_readers){
487 //Increment sharable count
488 ++this->m_ctrl.num_upr_shar;
492 inline bool interprocess_upgradable_mutex::timed_lock_sharable
493 (const boost::posix_time::ptime &abs_time)
495 //Mutexes and condvars handle just fine infinite abs_times
496 //so avoid checking it here
497 scoped_lock_t lck(m_mut, abs_time);
498 if(!lck.owns()) return false;
500 //The sharable lock must block in the first gate
501 //if an exclusive lock has been acquired
502 //or there are too many sharable locks
503 while (this->m_ctrl.exclusive_in
504 || this->m_ctrl.num_upr_shar == constants::max_readers){
505 if(!this->m_first_gate.timed_wait(lck, abs_time)){
506 if(this->m_ctrl.exclusive_in
507 || this->m_ctrl.num_upr_shar == constants::max_readers){
514 //Increment sharable count
515 ++this->m_ctrl.num_upr_shar;
519 inline void interprocess_upgradable_mutex::unlock_sharable()
521 scoped_lock_t lck(m_mut);
522 //Decrement sharable count
523 --this->m_ctrl.num_upr_shar;
524 if (this->m_ctrl.num_upr_shar == 0){
525 this->m_second_gate.notify_one();
527 //Check if there are blocked sharables because of
528 //there were too many sharables
529 else if(this->m_ctrl.num_upr_shar == (constants::max_readers-1)){
530 this->m_first_gate.notify_all();
536 inline void interprocess_upgradable_mutex::unlock_and_lock_upgradable()
538 scoped_lock_t lck(m_mut);
539 //Unmark it as exclusive
540 this->m_ctrl.exclusive_in = 0;
541 //Mark it as upgradable
542 this->m_ctrl.upgradable_in = 1;
543 //The sharable count should be 0 so increment it
544 this->m_ctrl.num_upr_shar = 1;
545 //Notify readers that they can enter
546 m_first_gate.notify_all();
549 inline void interprocess_upgradable_mutex::unlock_and_lock_sharable()
551 scoped_lock_t lck(m_mut);
552 //Unmark it as exclusive
553 this->m_ctrl.exclusive_in = 0;
554 //The sharable count should be 0 so increment it
555 this->m_ctrl.num_upr_shar = 1;
556 //Notify readers that they can enter
557 m_first_gate.notify_all();
560 inline void interprocess_upgradable_mutex::unlock_upgradable_and_lock_sharable()
562 scoped_lock_t lck(m_mut);
563 //Unmark it as upgradable (we don't have to decrement count)
564 this->m_ctrl.upgradable_in = 0;
565 //Notify readers/upgradable that they can enter
566 m_first_gate.notify_all();
571 inline void interprocess_upgradable_mutex::unlock_upgradable_and_lock()
573 scoped_lock_t lck(m_mut);
574 //Simulate unlock_upgradable() without
575 //notifying sharables.
576 this->m_ctrl.upgradable_in = 0;
577 --this->m_ctrl.num_upr_shar;
578 //Execute the second half of exclusive locking
579 this->m_ctrl.exclusive_in = 1;
582 upgradable_to_exclusive_rollback rollback(m_ctrl);
584 while (this->m_ctrl.num_upr_shar){
585 this->m_second_gate.wait(lck);
590 inline bool interprocess_upgradable_mutex::try_unlock_upgradable_and_lock()
592 scoped_lock_t lck(m_mut, try_to_lock);
593 //Check if there are no readers
595 || this->m_ctrl.num_upr_shar != 1){
598 //Now unlock upgradable and mark exclusive
599 this->m_ctrl.upgradable_in = 0;
600 --this->m_ctrl.num_upr_shar;
601 this->m_ctrl.exclusive_in = 1;
605 inline bool interprocess_upgradable_mutex::timed_unlock_upgradable_and_lock
606 (const boost::posix_time::ptime &abs_time)
608 //Mutexes and condvars handle just fine infinite abs_times
609 //so avoid checking it here
610 scoped_lock_t lck(m_mut, abs_time);
611 if(!lck.owns()) return false;
613 //Simulate unlock_upgradable() without
614 //notifying sharables.
615 this->m_ctrl.upgradable_in = 0;
616 --this->m_ctrl.num_upr_shar;
617 //Execute the second half of exclusive locking
618 this->m_ctrl.exclusive_in = 1;
621 upgradable_to_exclusive_rollback rollback(m_ctrl);
623 while (this->m_ctrl.num_upr_shar){
624 if(!this->m_second_gate.timed_wait(lck, abs_time)){
625 if(this->m_ctrl.num_upr_shar){
635 inline bool interprocess_upgradable_mutex::try_unlock_sharable_and_lock()
637 scoped_lock_t lck(m_mut, try_to_lock);
639 //If we can't lock or any has there is any exclusive, upgradable
640 //or sharable mark return false;
642 || this->m_ctrl.exclusive_in
643 || this->m_ctrl.upgradable_in
644 || this->m_ctrl.num_upr_shar != 1){
647 this->m_ctrl.exclusive_in = 1;
648 this->m_ctrl.num_upr_shar = 0;
652 inline bool interprocess_upgradable_mutex::try_unlock_sharable_and_lock_upgradable()
654 scoped_lock_t lck(m_mut, try_to_lock);
656 //The upgradable lock must fail
657 //if an exclusive or upgradable lock has been acquired
659 || this->m_ctrl.exclusive_in
660 || this->m_ctrl.upgradable_in){
664 //Mark that upgradable lock has been acquired
665 this->m_ctrl.upgradable_in = 1;
669 #endif //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED
671 } //namespace interprocess {
672 } //namespace boost {
675 #include <boost/interprocess/detail/config_end.hpp>
677 #endif //BOOST_INTERPROCESS_UPGRADABLE_MUTEX_HPP