Chris@16: ////////////////////////////////////////////////////////////////////////////// Chris@16: // Code based on Howard Hinnant's shared_mutex class Chris@16: // Chris@16: // (C) Copyright Howard Hinnant 2007-2010. Distributed under the Boost Chris@16: // Software License, Version 1.0. (see http://www.boost.org/LICENSE_1_0.txt) Chris@16: // Chris@16: // (C) Copyright Ion Gaztanaga 2005-2012. Distributed under the Boost Chris@16: // Software License, Version 1.0. (See accompanying file Chris@16: // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) Chris@16: // Chris@16: // See http://www.boost.org/libs/interprocess for documentation. Chris@16: // Chris@16: ////////////////////////////////////////////////////////////////////////////// Chris@16: Chris@16: #ifndef BOOST_INTERPROCESS_SHARABLE_MUTEX_HPP Chris@16: #define BOOST_INTERPROCESS_SHARABLE_MUTEX_HPP Chris@16: Chris@101: #ifndef BOOST_CONFIG_HPP Chris@101: # include Chris@101: #endif Chris@101: # Chris@101: #if defined(BOOST_HAS_PRAGMA_ONCE) Chris@16: # pragma once Chris@16: #endif Chris@16: Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: Chris@16: Chris@16: //!\file Chris@16: //!Describes interprocess_sharable_mutex class Chris@16: Chris@16: namespace boost { Chris@16: namespace interprocess { Chris@16: Chris@16: //!Wraps a interprocess_sharable_mutex that can be placed in shared memory and can be Chris@16: //!shared between processes. Allows timed lock tries Chris@16: class interprocess_sharable_mutex Chris@16: { Chris@16: //Non-copyable Chris@16: interprocess_sharable_mutex(const interprocess_sharable_mutex &); Chris@16: interprocess_sharable_mutex &operator=(const interprocess_sharable_mutex &); Chris@16: Chris@16: friend class interprocess_condition; Chris@16: public: Chris@16: Chris@16: //!Constructs the sharable lock. Chris@16: //!Throws interprocess_exception on error. Chris@16: interprocess_sharable_mutex(); Chris@16: Chris@16: //!Destroys the sharable lock. Chris@16: //!Does not throw. Chris@16: ~interprocess_sharable_mutex(); Chris@16: Chris@16: //Exclusive locking Chris@16: Chris@16: //!Effects: The calling thread tries to obtain exclusive ownership of the mutex, Chris@16: //! and if another thread has exclusive or sharable ownership of Chris@16: //! the mutex, it waits until it can obtain the ownership. Chris@16: //!Throws: interprocess_exception on error. Chris@16: void lock(); Chris@16: Chris@16: //!Effects: The calling thread tries to acquire exclusive ownership of the mutex Chris@16: //! without waiting. If no other thread has exclusive or sharable Chris@16: //! ownership of the mutex this succeeds. Chris@16: //!Returns: If it can acquire exclusive ownership immediately returns true. Chris@16: //! If it has to wait, returns false. Chris@16: //!Throws: interprocess_exception on error. Chris@16: bool try_lock(); Chris@16: Chris@16: //!Effects: The calling thread tries to acquire exclusive ownership of the mutex Chris@16: //! waiting if necessary until no other thread has exclusive or sharable Chris@16: //! ownership of the mutex or abs_time is reached. Chris@16: //!Returns: If acquires exclusive ownership, returns true. Otherwise returns false. Chris@16: //!Throws: interprocess_exception on error. Chris@16: bool timed_lock(const boost::posix_time::ptime &abs_time); Chris@16: Chris@16: //!Precondition: The thread must have exclusive ownership of the mutex. Chris@16: //!Effects: The calling thread releases the exclusive ownership of the mutex. Chris@16: //!Throws: An exception derived from interprocess_exception on error. Chris@16: void unlock(); Chris@16: Chris@16: //Sharable locking Chris@16: Chris@16: //!Effects: The calling thread tries to obtain sharable ownership of the mutex, Chris@16: //! and if another thread has exclusive ownership of the mutex, Chris@16: //! waits until it can obtain the ownership. Chris@16: //!Throws: interprocess_exception on error. Chris@16: void lock_sharable(); Chris@16: Chris@16: //!Effects: The calling thread tries to acquire sharable ownership of the mutex Chris@16: //! without waiting. If no other thread has exclusive ownership Chris@16: //! of the mutex this succeeds. Chris@16: //!Returns: If it can acquire sharable ownership immediately returns true. If it Chris@16: //! has to wait, returns false. Chris@16: //!Throws: interprocess_exception on error. Chris@16: bool try_lock_sharable(); Chris@16: Chris@16: //!Effects: The calling thread tries to acquire sharable ownership of the mutex Chris@16: //! waiting if necessary until no other thread has exclusive Chris@16: //! ownership of the mutex or abs_time is reached. Chris@16: //!Returns: If acquires sharable ownership, returns true. Otherwise returns false. Chris@16: //!Throws: interprocess_exception on error. Chris@16: bool timed_lock_sharable(const boost::posix_time::ptime &abs_time); Chris@16: Chris@16: //!Precondition: The thread must have sharable ownership of the mutex. Chris@16: //!Effects: The calling thread releases the sharable ownership of the mutex. Chris@16: //!Throws: An exception derived from interprocess_exception on error. Chris@16: void unlock_sharable(); Chris@16: Chris@101: #if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED) Chris@16: private: Chris@16: typedef scoped_lock scoped_lock_t; Chris@16: Chris@16: //Pack all the control data in a word to be able Chris@16: //to use atomic instructions in the future Chris@16: struct control_word_t Chris@16: { Chris@16: unsigned exclusive_in : 1; Chris@16: unsigned num_shared : sizeof(unsigned)*CHAR_BIT-1; Chris@16: } m_ctrl; Chris@16: Chris@16: interprocess_mutex m_mut; Chris@16: interprocess_condition m_first_gate; Chris@16: interprocess_condition m_second_gate; Chris@16: Chris@16: private: Chris@16: //Rollback structures for exceptions or failure return values Chris@16: struct exclusive_rollback Chris@16: { Chris@16: exclusive_rollback(control_word_t &ctrl Chris@16: ,interprocess_condition &first_gate) Chris@16: : mp_ctrl(&ctrl), m_first_gate(first_gate) Chris@16: {} Chris@16: Chris@16: void release() Chris@16: { mp_ctrl = 0; } Chris@16: Chris@16: ~exclusive_rollback() Chris@16: { Chris@16: if(mp_ctrl){ Chris@16: mp_ctrl->exclusive_in = 0; Chris@16: m_first_gate.notify_all(); Chris@16: } Chris@16: } Chris@16: control_word_t *mp_ctrl; Chris@16: interprocess_condition &m_first_gate; Chris@16: }; Chris@16: Chris@16: template Chris@16: struct base_constants_t Chris@16: { Chris@16: static const unsigned max_readers Chris@16: = ~(unsigned(1) << (sizeof(unsigned)*CHAR_BIT-1)); Chris@16: }; Chris@16: typedef base_constants_t<0> constants; Chris@101: #endif //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED Chris@16: }; Chris@16: Chris@101: #if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED) Chris@16: Chris@16: template Chris@16: const unsigned interprocess_sharable_mutex::base_constants_t::max_readers; Chris@16: Chris@16: inline interprocess_sharable_mutex::interprocess_sharable_mutex() Chris@16: { Chris@16: this->m_ctrl.exclusive_in = 0; Chris@16: this->m_ctrl.num_shared = 0; Chris@16: } Chris@16: Chris@16: inline interprocess_sharable_mutex::~interprocess_sharable_mutex() Chris@16: {} Chris@16: Chris@16: inline void interprocess_sharable_mutex::lock() Chris@16: { Chris@16: scoped_lock_t lck(m_mut); Chris@16: Chris@16: //The exclusive lock must block in the first gate Chris@16: //if an exclusive lock has been acquired Chris@16: while (this->m_ctrl.exclusive_in){ Chris@16: this->m_first_gate.wait(lck); Chris@16: } Chris@16: Chris@16: //Mark that exclusive lock has been acquired Chris@16: this->m_ctrl.exclusive_in = 1; Chris@16: Chris@16: //Prepare rollback Chris@16: exclusive_rollback rollback(this->m_ctrl, this->m_first_gate); Chris@16: Chris@16: //Now wait until all readers are gone Chris@16: while (this->m_ctrl.num_shared){ Chris@16: this->m_second_gate.wait(lck); Chris@16: } Chris@16: rollback.release(); Chris@16: } Chris@16: Chris@16: inline bool interprocess_sharable_mutex::try_lock() Chris@16: { Chris@16: scoped_lock_t lck(m_mut, try_to_lock); Chris@16: Chris@16: //If we can't lock or any has there is any exclusive Chris@16: //or sharable mark return false; Chris@16: if(!lck.owns() Chris@16: || this->m_ctrl.exclusive_in Chris@16: || this->m_ctrl.num_shared){ Chris@16: return false; Chris@16: } Chris@16: this->m_ctrl.exclusive_in = 1; Chris@16: return true; Chris@16: } Chris@16: Chris@16: inline bool interprocess_sharable_mutex::timed_lock Chris@16: (const boost::posix_time::ptime &abs_time) Chris@16: { Chris@16: scoped_lock_t lck(m_mut, abs_time); Chris@16: if(!lck.owns()) return false; Chris@16: Chris@16: //The exclusive lock must block in the first gate Chris@16: //if an exclusive lock has been acquired Chris@16: while (this->m_ctrl.exclusive_in){ Chris@101: //Mutexes and condvars handle just fine infinite abs_times Chris@101: //so avoid checking it here Chris@16: if(!this->m_first_gate.timed_wait(lck, abs_time)){ Chris@16: if(this->m_ctrl.exclusive_in){ Chris@16: return false; Chris@16: } Chris@16: break; Chris@16: } Chris@16: } Chris@16: Chris@16: //Mark that exclusive lock has been acquired Chris@16: this->m_ctrl.exclusive_in = 1; Chris@16: Chris@16: //Prepare rollback Chris@16: exclusive_rollback rollback(this->m_ctrl, this->m_first_gate); Chris@16: Chris@16: //Now wait until all readers are gone Chris@16: while (this->m_ctrl.num_shared){ Chris@101: //Mutexes and condvars handle just fine infinite abs_times Chris@101: //so avoid checking it here Chris@16: if(!this->m_second_gate.timed_wait(lck, abs_time)){ Chris@16: if(this->m_ctrl.num_shared){ Chris@16: return false; Chris@16: } Chris@16: break; Chris@16: } Chris@16: } Chris@16: rollback.release(); Chris@16: return true; Chris@16: } Chris@16: Chris@16: inline void interprocess_sharable_mutex::unlock() Chris@16: { Chris@16: scoped_lock_t lck(m_mut); Chris@16: this->m_ctrl.exclusive_in = 0; Chris@16: this->m_first_gate.notify_all(); Chris@16: } Chris@16: Chris@16: //Sharable locking Chris@16: Chris@16: inline void interprocess_sharable_mutex::lock_sharable() Chris@16: { Chris@16: scoped_lock_t lck(m_mut); Chris@16: Chris@16: //The sharable lock must block in the first gate Chris@16: //if an exclusive lock has been acquired Chris@16: //or there are too many sharable locks Chris@16: while(this->m_ctrl.exclusive_in Chris@16: || this->m_ctrl.num_shared == constants::max_readers){ Chris@16: this->m_first_gate.wait(lck); Chris@16: } Chris@16: Chris@16: //Increment sharable count Chris@16: ++this->m_ctrl.num_shared; Chris@16: } Chris@16: Chris@16: inline bool interprocess_sharable_mutex::try_lock_sharable() Chris@16: { Chris@16: scoped_lock_t lck(m_mut, try_to_lock); Chris@16: Chris@16: //The sharable lock must fail Chris@16: //if an exclusive lock has been acquired Chris@16: //or there are too many sharable locks Chris@16: if(!lck.owns() Chris@16: || this->m_ctrl.exclusive_in Chris@16: || this->m_ctrl.num_shared == constants::max_readers){ Chris@16: return false; Chris@16: } Chris@16: Chris@16: //Increment sharable count Chris@16: ++this->m_ctrl.num_shared; Chris@16: return true; Chris@16: } Chris@16: Chris@16: inline bool interprocess_sharable_mutex::timed_lock_sharable Chris@16: (const boost::posix_time::ptime &abs_time) Chris@16: { Chris@16: scoped_lock_t lck(m_mut, abs_time); Chris@16: if(!lck.owns()) return false; Chris@16: Chris@16: //The sharable lock must block in the first gate Chris@16: //if an exclusive lock has been acquired Chris@16: //or there are too many sharable locks Chris@16: while (this->m_ctrl.exclusive_in Chris@16: || this->m_ctrl.num_shared == constants::max_readers){ Chris@101: //Mutexes and condvars handle just fine infinite abs_times Chris@101: //so avoid checking it here Chris@16: if(!this->m_first_gate.timed_wait(lck, abs_time)){ Chris@16: if(this->m_ctrl.exclusive_in Chris@16: || this->m_ctrl.num_shared == constants::max_readers){ Chris@16: return false; Chris@16: } Chris@16: break; Chris@16: } Chris@16: } Chris@16: Chris@16: //Increment sharable count Chris@16: ++this->m_ctrl.num_shared; Chris@16: return true; Chris@16: } Chris@16: Chris@16: inline void interprocess_sharable_mutex::unlock_sharable() Chris@16: { Chris@16: scoped_lock_t lck(m_mut); Chris@16: //Decrement sharable count Chris@16: --this->m_ctrl.num_shared; Chris@16: if (this->m_ctrl.num_shared == 0){ Chris@16: this->m_second_gate.notify_one(); Chris@16: } Chris@16: //Check if there are blocked sharables because of Chris@16: //there were too many sharables Chris@16: else if(this->m_ctrl.num_shared == (constants::max_readers-1)){ Chris@16: this->m_first_gate.notify_all(); Chris@16: } Chris@16: } Chris@16: Chris@101: #endif //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED Chris@16: Chris@16: } //namespace interprocess { Chris@16: } //namespace boost { Chris@16: Chris@16: #include Chris@16: Chris@16: #endif //BOOST_INTERPROCESS_SHARABLE_MUTEX_HPP