////////////////////////////////////////////////////////////////////////////// | |
// | |
// (C) Copyright Ion Gaztanaga 2005-2009. Distributed under the Boost | |
// Software License, Version 1.0. (See accompanying file | |
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |
// | |
// See http://www.boost.org/libs/interprocess for documentation. | |
// | |
////////////////////////////////////////////////////////////////////////////// | |
#ifndef BOOST_INTERPROCESS_UPGRADABLE_MUTEX_HPP | |
#define BOOST_INTERPROCESS_UPGRADABLE_MUTEX_HPP | |
#if (defined _MSC_VER) && (_MSC_VER >= 1200) | |
# pragma once | |
#endif | |
#include <boost/interprocess/detail/config_begin.hpp> | |
#include <boost/interprocess/detail/workaround.hpp> | |
#include <boost/interprocess/sync/scoped_lock.hpp> | |
#include <boost/interprocess/sync/interprocess_mutex.hpp> | |
#include <boost/interprocess/sync/interprocess_condition.hpp> | |
#include <climits> | |
//!\file | |
//!Describes interprocess_upgradable_mutex class | |
namespace boost { | |
namespace interprocess { | |
//!Wraps a interprocess_upgradable_mutex that can be placed in shared memory and can be | |
//!shared between processes. Allows timed lock tries | |
class interprocess_upgradable_mutex | |
{ | |
//Non-copyable | |
interprocess_upgradable_mutex(const interprocess_upgradable_mutex &); | |
interprocess_upgradable_mutex &operator=(const interprocess_upgradable_mutex &); | |
friend class interprocess_condition; | |
public: | |
//!Constructs the upgradable lock. | |
//!Throws interprocess_exception on error. | |
interprocess_upgradable_mutex(); | |
//!Destroys the upgradable lock. | |
//!Does not throw. | |
~interprocess_upgradable_mutex(); | |
//Exclusive locking | |
//!Effects: The calling thread tries to obtain exclusive ownership of the mutex, | |
//! and if another thread has exclusive, sharable or upgradable ownership of | |
//! the mutex, it waits until it can obtain the ownership. | |
//!Throws: interprocess_exception on error. | |
void lock(); | |
//!Effects: The calling thread tries to acquire exclusive ownership of the mutex | |
//! without waiting. If no other thread has exclusive, sharable or upgradable | |
//! ownership of the mutex this succeeds. | |
//!Returns: If it can acquire exclusive ownership immediately returns true. | |
//! If it has to wait, returns false. | |
//!Throws: interprocess_exception on error. | |
bool try_lock(); | |
//!Effects: The calling thread tries to acquire exclusive ownership of the mutex | |
//! waiting if necessary until no other thread has has exclusive, sharable or | |
//! upgradable ownership of the mutex or abs_time is reached. | |
//!Returns: If acquires exclusive ownership, returns true. Otherwise returns false. | |
//!Throws: interprocess_exception on error. | |
bool timed_lock(const boost::posix_time::ptime &abs_time); | |
//!Precondition: The thread must have exclusive ownership of the mutex. | |
//!Effects: The calling thread releases the exclusive ownership of the mutex. | |
//!Throws: An exception derived from interprocess_exception on error. | |
void unlock(); | |
//Sharable locking | |
//!Effects: The calling thread tries to obtain sharable ownership of the mutex, | |
//! and if another thread has exclusive or upgradable ownership of the mutex, | |
//! waits until it can obtain the ownership. | |
//!Throws: interprocess_exception on error. | |
void lock_sharable(); | |
//!Effects: The calling thread tries to acquire sharable ownership of the mutex | |
//! without waiting. If no other thread has has exclusive or upgradable ownership | |
//! of the mutex this succeeds. | |
//!Returns: If it can acquire sharable ownership immediately returns true. If it | |
//! has to wait, returns false. | |
//!Throws: interprocess_exception on error. | |
bool try_lock_sharable(); | |
//!Effects: The calling thread tries to acquire sharable ownership of the mutex | |
//! waiting if necessary until no other thread has has exclusive or upgradable | |
//! ownership of the mutex or abs_time is reached. | |
//!Returns: If acquires sharable ownership, returns true. Otherwise returns false. | |
//!Throws: interprocess_exception on error. | |
bool timed_lock_sharable(const boost::posix_time::ptime &abs_time); | |
//!Precondition: The thread must have sharable ownership of the mutex. | |
//!Effects: The calling thread releases the sharable ownership of the mutex. | |
//!Throws: An exception derived from interprocess_exception on error. | |
void unlock_sharable(); | |
//Upgradable locking | |
//!Effects: The calling thread tries to obtain upgradable ownership of the mutex, | |
//! and if another thread has exclusive or upgradable ownership of the mutex, | |
//! waits until it can obtain the ownership. | |
//!Throws: interprocess_exception on error. | |
void lock_upgradable(); | |
//!Effects: The calling thread tries to acquire upgradable ownership of the mutex | |
//! without waiting. If no other thread has has exclusive or upgradable ownership | |
//! of the mutex this succeeds. | |
//!Returns: If it can acquire upgradable ownership immediately returns true. | |
//! If it has to wait, returns false. | |
//!Throws: interprocess_exception on error. | |
bool try_lock_upgradable(); | |
//!Effects: The calling thread tries to acquire upgradable ownership of the mutex | |
//! waiting if necessary until no other thread has has exclusive or upgradable | |
//! ownership of the mutex or abs_time is reached. | |
//!Returns: If acquires upgradable ownership, returns true. Otherwise returns false. | |
//!Throws: interprocess_exception on error. | |
bool timed_lock_upgradable(const boost::posix_time::ptime &abs_time); | |
//!Precondition: The thread must have upgradable ownership of the mutex. | |
//!Effects: The calling thread releases the upgradable ownership of the mutex. | |
//!Throws: An exception derived from interprocess_exception on error. | |
void unlock_upgradable(); | |
//Demotions | |
//!Precondition: The thread must have exclusive ownership of the mutex. | |
//!Effects: The thread atomically releases exclusive ownership and acquires | |
//! upgradable ownership. This operation is non-blocking. | |
//!Throws: An exception derived from interprocess_exception on error. | |
void unlock_and_lock_upgradable(); | |
//!Precondition: The thread must have exclusive ownership of the mutex. | |
//!Effects: The thread atomically releases exclusive ownership and acquires | |
//! sharable ownership. This operation is non-blocking. | |
//!Throws: An exception derived from interprocess_exception on error. | |
void unlock_and_lock_sharable(); | |
//!Precondition: The thread must have upgradable ownership of the mutex. | |
//!Effects: The thread atomically releases upgradable ownership and acquires | |
//! sharable ownership. This operation is non-blocking. | |
//!Throws: An exception derived from interprocess_exception on error. | |
void unlock_upgradable_and_lock_sharable(); | |
//Promotions | |
//!Precondition: The thread must have upgradable ownership of the mutex. | |
//!Effects: The thread atomically releases upgradable ownership and acquires | |
//! exclusive ownership. This operation will block until all threads with | |
//! sharable ownership release their sharable lock. | |
//!Throws: An exception derived from interprocess_exception on error. | |
void unlock_upgradable_and_lock(); | |
//!Precondition: The thread must have upgradable ownership of the mutex. | |
//!Effects: The thread atomically releases upgradable ownership and tries to | |
//! acquire exclusive ownership. This operation will fail if there are threads | |
//! with sharable ownership, but it will maintain upgradable ownership. | |
//!Returns: If acquires exclusive ownership, returns true. Otherwise returns false. | |
//!Throws: An exception derived from interprocess_exception on error. | |
bool try_unlock_upgradable_and_lock(); | |
//!Precondition: The thread must have upgradable ownership of the mutex. | |
//!Effects: The thread atomically releases upgradable ownership and tries to acquire | |
//! exclusive ownership, waiting if necessary until abs_time. This operation will | |
//! fail if there are threads with sharable ownership or timeout reaches, but it | |
//! will maintain upgradable ownership. | |
//!Returns: If acquires exclusive ownership, returns true. Otherwise returns false. | |
//!Throws: An exception derived from interprocess_exception on error. */ | |
bool timed_unlock_upgradable_and_lock(const boost::posix_time::ptime &abs_time); | |
//!Precondition: The thread must have sharable ownership of the mutex. | |
//!Effects: The thread atomically releases sharable ownership and tries to acquire | |
//! exclusive ownership. This operation will fail if there are threads with sharable | |
//! or upgradable ownership, but it will maintain sharable ownership. | |
//!Returns: If acquires exclusive ownership, returns true. Otherwise returns false. | |
//!Throws: An exception derived from interprocess_exception on error. | |
bool try_unlock_sharable_and_lock(); | |
//!Precondition: The thread must have sharable ownership of the mutex. | |
//!Effects: The thread atomically releases sharable ownership and tries to acquire | |
//! upgradable ownership. This operation will fail if there are threads with sharable | |
//! or upgradable ownership, but it will maintain sharable ownership. | |
//!Returns: If acquires upgradable ownership, returns true. Otherwise returns false. | |
//!Throws: An exception derived from interprocess_exception on error. | |
bool try_unlock_sharable_and_lock_upgradable(); | |
/// @cond | |
private: | |
typedef scoped_lock<interprocess_mutex> scoped_lock_t; | |
//Pack all the control data in a word to be able | |
//to use atomic instructions in the future | |
struct control_word_t | |
{ | |
unsigned exclusive_in : 1; | |
unsigned upgradable_in : 1; | |
unsigned num_upr_shar : sizeof(unsigned)*CHAR_BIT-2; | |
} m_ctrl; | |
interprocess_mutex m_mut; | |
interprocess_condition m_first_gate; | |
interprocess_condition m_second_gate; | |
private: | |
//Rollback structures for exceptions or failure return values | |
struct exclusive_rollback | |
{ | |
exclusive_rollback(control_word_t &ctrl | |
,interprocess_condition &first_gate) | |
: mp_ctrl(&ctrl), m_first_gate(first_gate) | |
{} | |
void release() | |
{ mp_ctrl = 0; } | |
~exclusive_rollback() | |
{ | |
if(mp_ctrl){ | |
mp_ctrl->exclusive_in = 0; | |
m_first_gate.notify_all(); | |
} | |
} | |
control_word_t *mp_ctrl; | |
interprocess_condition &m_first_gate; | |
}; | |
struct upgradable_to_exclusive_rollback | |
{ | |
upgradable_to_exclusive_rollback(control_word_t &ctrl) | |
: mp_ctrl(&ctrl) | |
{} | |
void release() | |
{ mp_ctrl = 0; } | |
~upgradable_to_exclusive_rollback() | |
{ | |
if(mp_ctrl){ | |
//Recover upgradable lock | |
mp_ctrl->upgradable_in = 1; | |
++mp_ctrl->num_upr_shar; | |
//Execute the second half of exclusive locking | |
mp_ctrl->exclusive_in = 0; | |
} | |
} | |
control_word_t *mp_ctrl; | |
}; | |
template<int Dummy> | |
struct base_constants_t | |
{ | |
static const unsigned max_readers | |
= ~(unsigned(3) << (sizeof(unsigned)*CHAR_BIT-2)); | |
}; | |
typedef base_constants_t<0> constants; | |
/// @endcond | |
}; | |
/// @cond | |
template <int Dummy> | |
const unsigned interprocess_upgradable_mutex::base_constants_t<Dummy>::max_readers; | |
inline interprocess_upgradable_mutex::interprocess_upgradable_mutex() | |
{ | |
this->m_ctrl.exclusive_in = 0; | |
this->m_ctrl.upgradable_in = 0; | |
this->m_ctrl.num_upr_shar = 0; | |
} | |
inline interprocess_upgradable_mutex::~interprocess_upgradable_mutex() | |
{} | |
inline void interprocess_upgradable_mutex::lock() | |
{ | |
scoped_lock_t lock(m_mut); | |
//The exclusive lock must block in the first gate | |
//if an exclusive or upgradable lock has been acquired | |
while (this->m_ctrl.exclusive_in || this->m_ctrl.upgradable_in){ | |
this->m_first_gate.wait(lock); | |
} | |
//Mark that exclusive lock has been acquired | |
this->m_ctrl.exclusive_in = 1; | |
//Prepare rollback | |
exclusive_rollback rollback(this->m_ctrl, this->m_first_gate); | |
//Now wait until all readers are gone | |
while (this->m_ctrl.num_upr_shar){ | |
this->m_second_gate.wait(lock); | |
} | |
rollback.release(); | |
} | |
inline bool interprocess_upgradable_mutex::try_lock() | |
{ | |
scoped_lock_t lock(m_mut, try_to_lock); | |
//If we can't lock or any has there is any exclusive, upgradable | |
//or sharable mark return false; | |
if(!lock.owns() | |
|| this->m_ctrl.exclusive_in | |
|| this->m_ctrl.num_upr_shar){ | |
return false; | |
} | |
this->m_ctrl.exclusive_in = 1; | |
return true; | |
} | |
inline bool interprocess_upgradable_mutex::timed_lock | |
(const boost::posix_time::ptime &abs_time) | |
{ | |
if(abs_time == boost::posix_time::pos_infin){ | |
this->lock(); | |
return true; | |
} | |
scoped_lock_t lock(m_mut, abs_time); | |
if(!lock.owns()) return false; | |
//The exclusive lock must block in the first gate | |
//if an exclusive or upgradable lock has been acquired | |
while (this->m_ctrl.exclusive_in || this->m_ctrl.upgradable_in){ | |
if(!this->m_first_gate.timed_wait(lock, abs_time)) | |
return !(this->m_ctrl.exclusive_in || this->m_ctrl.upgradable_in); | |
} | |
//Mark that exclusive lock has been acquired | |
this->m_ctrl.exclusive_in = 1; | |
//Prepare rollback | |
exclusive_rollback rollback(this->m_ctrl, this->m_first_gate); | |
//Now wait until all readers are gone | |
while (this->m_ctrl.num_upr_shar){ | |
if(!this->m_second_gate.timed_wait(lock, abs_time)){ | |
return !(this->m_ctrl.num_upr_shar); | |
} | |
} | |
rollback.release(); | |
return true; | |
} | |
inline void interprocess_upgradable_mutex::unlock() | |
{ | |
scoped_lock_t lock(m_mut); | |
this->m_ctrl.exclusive_in = 0; | |
this->m_first_gate.notify_all(); | |
} | |
//Upgradable locking | |
inline void interprocess_upgradable_mutex::lock_upgradable() | |
{ | |
scoped_lock_t lock(m_mut); | |
//The upgradable lock must block in the first gate | |
//if an exclusive or upgradable lock has been acquired | |
//or there are too many sharable locks | |
while(this->m_ctrl.exclusive_in || this->m_ctrl.upgradable_in | |
|| this->m_ctrl.num_upr_shar == constants::max_readers){ | |
this->m_first_gate.wait(lock); | |
} | |
//Mark that upgradable lock has been acquired | |
//And add upgradable to the sharable count | |
this->m_ctrl.upgradable_in = 1; | |
++this->m_ctrl.num_upr_shar; | |
} | |
inline bool interprocess_upgradable_mutex::try_lock_upgradable() | |
{ | |
scoped_lock_t lock(m_mut, try_to_lock); | |
//The upgradable lock must fail | |
//if an exclusive or upgradable lock has been acquired | |
//or there are too many sharable locks | |
if(!lock.owns() | |
|| this->m_ctrl.exclusive_in | |
|| this->m_ctrl.upgradable_in | |
|| this->m_ctrl.num_upr_shar == constants::max_readers){ | |
return false; | |
} | |
//Mark that upgradable lock has been acquired | |
//And add upgradable to the sharable count | |
this->m_ctrl.upgradable_in = 1; | |
++this->m_ctrl.num_upr_shar; | |
return true; | |
} | |
inline bool interprocess_upgradable_mutex::timed_lock_upgradable | |
(const boost::posix_time::ptime &abs_time) | |
{ | |
if(abs_time == boost::posix_time::pos_infin){ | |
this->lock_upgradable(); | |
return true; | |
} | |
scoped_lock_t lock(m_mut, abs_time); | |
if(!lock.owns()) return false; | |
//The upgradable lock must block in the first gate | |
//if an exclusive or upgradable lock has been acquired | |
//or there are too many sharable locks | |
while(this->m_ctrl.exclusive_in | |
|| this->m_ctrl.upgradable_in | |
|| this->m_ctrl.num_upr_shar == constants::max_readers){ | |
if(!this->m_first_gate.timed_wait(lock, abs_time)){ | |
return!(this->m_ctrl.exclusive_in | |
|| this->m_ctrl.upgradable_in | |
|| this->m_ctrl.num_upr_shar == constants::max_readers); | |
} | |
} | |
//Mark that upgradable lock has been acquired | |
//And add upgradable to the sharable count | |
this->m_ctrl.upgradable_in = 1; | |
++this->m_ctrl.num_upr_shar; | |
return true; | |
} | |
inline void interprocess_upgradable_mutex::unlock_upgradable() | |
{ | |
scoped_lock_t lock(m_mut); | |
//Mark that upgradable lock has been acquired | |
//And add upgradable to the sharable count | |
this->m_ctrl.upgradable_in = 0; | |
--this->m_ctrl.num_upr_shar; | |
this->m_first_gate.notify_all(); | |
} | |
//Sharable locking | |
inline void interprocess_upgradable_mutex::lock_sharable() | |
{ | |
scoped_lock_t lock(m_mut); | |
//The sharable lock must block in the first gate | |
//if an exclusive lock has been acquired | |
//or there are too many sharable locks | |
while(this->m_ctrl.exclusive_in | |
|| this->m_ctrl.num_upr_shar == constants::max_readers){ | |
this->m_first_gate.wait(lock); | |
} | |
//Increment sharable count | |
++this->m_ctrl.num_upr_shar; | |
} | |
inline bool interprocess_upgradable_mutex::try_lock_sharable() | |
{ | |
scoped_lock_t lock(m_mut, try_to_lock); | |
//The sharable lock must fail | |
//if an exclusive lock has been acquired | |
//or there are too many sharable locks | |
if(!lock.owns() | |
|| this->m_ctrl.exclusive_in | |
|| this->m_ctrl.num_upr_shar == constants::max_readers){ | |
return false; | |
} | |
//Increment sharable count | |
++this->m_ctrl.num_upr_shar; | |
return true; | |
} | |
inline bool interprocess_upgradable_mutex::timed_lock_sharable | |
(const boost::posix_time::ptime &abs_time) | |
{ | |
if(abs_time == boost::posix_time::pos_infin){ | |
this->lock_sharable(); | |
return true; | |
} | |
scoped_lock_t lock(m_mut, abs_time); | |
if(!lock.owns()) return false; | |
//The sharable lock must block in the first gate | |
//if an exclusive lock has been acquired | |
//or there are too many sharable locks | |
while (this->m_ctrl.exclusive_in | |
|| this->m_ctrl.num_upr_shar == constants::max_readers){ | |
if(!this->m_first_gate.timed_wait(lock, abs_time)){ | |
return!(this->m_ctrl.exclusive_in | |
|| this->m_ctrl.num_upr_shar == constants::max_readers); | |
} | |
} | |
//Increment sharable count | |
++this->m_ctrl.num_upr_shar; | |
return true; | |
} | |
inline void interprocess_upgradable_mutex::unlock_sharable() | |
{ | |
scoped_lock_t lock(m_mut); | |
//Decrement sharable count | |
--this->m_ctrl.num_upr_shar; | |
if (this->m_ctrl.num_upr_shar == 0){ | |
this->m_second_gate.notify_one(); | |
} | |
//Check if there are blocked sharables because of | |
//there were too many sharables | |
else if(this->m_ctrl.num_upr_shar == (constants::max_readers-1)){ | |
this->m_first_gate.notify_all(); | |
} | |
} | |
//Downgrading | |
inline void interprocess_upgradable_mutex::unlock_and_lock_upgradable() | |
{ | |
scoped_lock_t lock(m_mut); | |
//Unmark it as exclusive | |
this->m_ctrl.exclusive_in = 0; | |
//Mark it as upgradable | |
this->m_ctrl.upgradable_in = 1; | |
//The sharable count should be 0 so increment it | |
this->m_ctrl.num_upr_shar = 1; | |
//Notify readers that they can enter | |
m_first_gate.notify_all(); | |
} | |
inline void interprocess_upgradable_mutex::unlock_and_lock_sharable() | |
{ | |
scoped_lock_t lock(m_mut); | |
//Unmark it as exclusive | |
this->m_ctrl.exclusive_in = 0; | |
//The sharable count should be 0 so increment it | |
this->m_ctrl.num_upr_shar = 1; | |
//Notify readers that they can enter | |
m_first_gate.notify_all(); | |
} | |
inline void interprocess_upgradable_mutex::unlock_upgradable_and_lock_sharable() | |
{ | |
scoped_lock_t lock(m_mut); | |
//Unmark it as upgradable (we don't have to decrement count) | |
this->m_ctrl.upgradable_in = 0; | |
//Notify readers/upgradable that they can enter | |
m_first_gate.notify_all(); | |
} | |
//Upgrading | |
inline void interprocess_upgradable_mutex::unlock_upgradable_and_lock() | |
{ | |
scoped_lock_t lock(m_mut); | |
//Simulate unlock_upgradable() without | |
//notifying sharables. | |
this->m_ctrl.upgradable_in = 0; | |
--this->m_ctrl.num_upr_shar; | |
//Execute the second half of exclusive locking | |
this->m_ctrl.exclusive_in = 1; | |
//Prepare rollback | |
upgradable_to_exclusive_rollback rollback(m_ctrl); | |
while (this->m_ctrl.num_upr_shar){ | |
this->m_second_gate.wait(lock); | |
} | |
rollback.release(); | |
} | |
inline bool interprocess_upgradable_mutex::try_unlock_upgradable_and_lock() | |
{ | |
scoped_lock_t lock(m_mut, try_to_lock); | |
//Check if there are no readers | |
if(!lock.owns() | |
|| this->m_ctrl.num_upr_shar != 1){ | |
return false; | |
} | |
//Now unlock upgradable and mark exclusive | |
this->m_ctrl.upgradable_in = 0; | |
--this->m_ctrl.num_upr_shar; | |
this->m_ctrl.exclusive_in = 1; | |
return true; | |
} | |
inline bool interprocess_upgradable_mutex::timed_unlock_upgradable_and_lock | |
(const boost::posix_time::ptime &abs_time) | |
{ | |
scoped_lock_t lock(m_mut, abs_time); | |
if(!lock.owns()) return false; | |
//Simulate unlock_upgradable() without | |
//notifying sharables. | |
this->m_ctrl.upgradable_in = 0; | |
--this->m_ctrl.num_upr_shar; | |
//Execute the second half of exclusive locking | |
this->m_ctrl.exclusive_in = 1; | |
//Prepare rollback | |
upgradable_to_exclusive_rollback rollback(m_ctrl); | |
while (this->m_ctrl.num_upr_shar){ | |
if(!this->m_second_gate.timed_wait(lock, abs_time)){ | |
return !(this->m_ctrl.num_upr_shar); | |
} | |
} | |
rollback.release(); | |
return true; | |
} | |
inline bool interprocess_upgradable_mutex::try_unlock_sharable_and_lock() | |
{ | |
scoped_lock_t lock(m_mut, try_to_lock); | |
//If we can't lock or any has there is any exclusive, upgradable | |
//or sharable mark return false; | |
if(!lock.owns() | |
|| this->m_ctrl.exclusive_in | |
|| this->m_ctrl.upgradable_in | |
|| this->m_ctrl.num_upr_shar != 1){ | |
return false; | |
} | |
this->m_ctrl.exclusive_in = 1; | |
this->m_ctrl.num_upr_shar = 0; | |
return true; | |
} | |
inline bool interprocess_upgradable_mutex::try_unlock_sharable_and_lock_upgradable() | |
{ | |
scoped_lock_t lock(m_mut, try_to_lock); | |
//The upgradable lock must fail | |
//if an exclusive or upgradable lock has been acquired | |
if(!lock.owns() | |
|| this->m_ctrl.exclusive_in | |
|| this->m_ctrl.upgradable_in){ | |
return false; | |
} | |
//Mark that upgradable lock has been acquired | |
this->m_ctrl.upgradable_in = 1; | |
return true; | |
} | |
/// @endcond | |
} //namespace interprocess { | |
} //namespace boost { | |
#include <boost/interprocess/detail/config_end.hpp> | |
#endif //BOOST_INTERPROCESS_UPGRADABLE_MUTEX_HPP |