diff options
author | Anas Nashif <anas.nashif@intel.com> | 2012-10-30 12:57:26 -0700 |
---|---|---|
committer | Anas Nashif <anas.nashif@intel.com> | 2012-10-30 12:57:26 -0700 |
commit | 1a78a62555be32868418fe52f8e330c9d0f95d5a (patch) | |
tree | d3765a80e7d3b9640ec2e930743630cd6b9fce2b /boost/interprocess/sync | |
download | boost-1a78a62555be32868418fe52f8e330c9d0f95d5a.tar.gz boost-1a78a62555be32868418fe52f8e330c9d0f95d5a.tar.bz2 boost-1a78a62555be32868418fe52f8e330c9d0f95d5a.zip |
Imported Upstream version 1.49.0upstream/1.49.0
Diffstat (limited to 'boost/interprocess/sync')
45 files changed, 8535 insertions, 0 deletions
diff --git a/boost/interprocess/sync/file_lock.hpp b/boost/interprocess/sync/file_lock.hpp new file mode 100644 index 0000000000..a60b814a4f --- /dev/null +++ b/boost/interprocess/sync/file_lock.hpp @@ -0,0 +1,305 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2005-2011. 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_FILE_LOCK_HPP +#define BOOST_INTERPROCESS_FILE_LOCK_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/exceptions.hpp> +#include <boost/interprocess/detail/os_file_functions.hpp> +#include <boost/interprocess/detail/os_thread_functions.hpp> +#include <boost/interprocess/detail/posix_time_types_wrk.hpp> +#include <boost/move/move.hpp> + +//!\file +//!Describes a class that wraps file locking capabilities. + +namespace boost { +namespace interprocess { + + +//!A file lock, is a mutual exclusion utility similar to a mutex using a +//!file. A file lock has sharable and exclusive locking capabilities and +//!can be used with scoped_lock and sharable_lock classes. +//!A file lock can't guarantee synchronization between threads of the same +//!process so just use file locks to synchronize threads from different processes. +class file_lock +{ + /// @cond + //Non-copyable + BOOST_MOVABLE_BUT_NOT_COPYABLE(file_lock) + /// @endcond + + public: + //!Constructs an empty file mapping. + //!Does not throw + file_lock() + : m_file_hnd(file_handle_t(ipcdetail::invalid_file())) + {} + + //!Opens a file lock. Throws interprocess_exception if the file does not + //!exist or there are no operating system resources. + file_lock(const char *name); + + //!Moves the ownership of "moved"'s file mapping object to *this. + //!After the call, "moved" does not represent any file mapping object. + //!Does not throw + file_lock(BOOST_RV_REF(file_lock) moved) + : m_file_hnd(file_handle_t(ipcdetail::invalid_file())) + { this->swap(moved); } + + //!Moves the ownership of "moved"'s file mapping to *this. + //!After the call, "moved" does not represent any file mapping. + //!Does not throw + file_lock &operator=(BOOST_RV_REF(file_lock) moved) + { + file_lock tmp(boost::move(moved)); + this->swap(tmp); + return *this; + } + + //!Closes a file lock. Does not throw. + ~file_lock(); + + //!Swaps two file_locks. + //!Does not throw. + void swap(file_lock &other) + { + file_handle_t tmp = m_file_hnd; + m_file_hnd = other.m_file_hnd; + other.m_file_hnd = tmp; + } + + //Exclusive locking + + //!Effects: The calling thread tries to obtain exclusive ownership of the mutex, + //! and if another thread has exclusive, or sharable 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, or sharable + //! 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 exclusive, or sharable + //! 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 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 exclusive 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 exclusive 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(); + /// @cond + private: + file_handle_t m_file_hnd; + + bool timed_acquire_file_lock + (file_handle_t hnd, bool &acquired, const boost::posix_time::ptime &abs_time) + { + //Obtain current count and target time + boost::posix_time::ptime now = microsec_clock::universal_time(); + using namespace boost::detail; + + if(now >= abs_time) return false; + + do{ + if(!ipcdetail::try_acquire_file_lock(hnd, acquired)) + return false; + + if(acquired) + return true; + else{ + now = microsec_clock::universal_time(); + + if(now >= abs_time){ + acquired = false; + return true; + } + // relinquish current time slice + ipcdetail::thread_yield(); + } + }while (true); + } + + bool timed_acquire_file_lock_sharable + (file_handle_t hnd, bool &acquired, const boost::posix_time::ptime &abs_time) + { + //Obtain current count and target time + boost::posix_time::ptime now = microsec_clock::universal_time(); + using namespace boost::detail; + + if(now >= abs_time) return false; + + do{ + if(!ipcdetail::try_acquire_file_lock_sharable(hnd, acquired)) + return false; + + if(acquired) + return true; + else{ + now = microsec_clock::universal_time(); + + if(now >= abs_time){ + acquired = false; + return true; + } + // relinquish current time slice + ipcdetail::thread_yield(); + } + }while (true); + } + /// @endcond +}; + +inline file_lock::file_lock(const char *name) +{ + m_file_hnd = ipcdetail::open_existing_file(name, read_write); + + if(m_file_hnd == ipcdetail::invalid_file()){ + error_info err(system_error_code()); + throw interprocess_exception(err); + } +} + +inline file_lock::~file_lock() +{ + if(m_file_hnd != ipcdetail::invalid_file()){ + ipcdetail::close_file(m_file_hnd); + m_file_hnd = ipcdetail::invalid_file(); + } +} + +inline void file_lock::lock() +{ + if(!ipcdetail::acquire_file_lock(m_file_hnd)){ + error_info err(system_error_code()); + throw interprocess_exception(err); + } +} + +inline bool file_lock::try_lock() +{ + bool result; + if(!ipcdetail::try_acquire_file_lock(m_file_hnd, result)){ + error_info err(system_error_code()); + throw interprocess_exception(err); + } + return result; +} + +inline bool file_lock::timed_lock(const boost::posix_time::ptime &abs_time) +{ + if(abs_time == boost::posix_time::pos_infin){ + this->lock(); + return true; + } + bool result; + if(!this->timed_acquire_file_lock(m_file_hnd, result, abs_time)){ + error_info err(system_error_code()); + throw interprocess_exception(err); + } + return result; +} + +inline void file_lock::unlock() +{ + if(!ipcdetail::release_file_lock(m_file_hnd)){ + error_info err(system_error_code()); + throw interprocess_exception(err); + } +} + +inline void file_lock::lock_sharable() +{ + if(!ipcdetail::acquire_file_lock_sharable(m_file_hnd)){ + error_info err(system_error_code()); + throw interprocess_exception(err); + } +} + +inline bool file_lock::try_lock_sharable() +{ + bool result; + if(!ipcdetail::try_acquire_file_lock_sharable(m_file_hnd, result)){ + error_info err(system_error_code()); + throw interprocess_exception(err); + } + return result; +} + +inline bool file_lock::timed_lock_sharable(const boost::posix_time::ptime &abs_time) +{ + if(abs_time == boost::posix_time::pos_infin){ + this->lock_sharable(); + return true; + } + bool result; + if(!this->timed_acquire_file_lock_sharable(m_file_hnd, result, abs_time)){ + error_info err(system_error_code()); + throw interprocess_exception(err); + } + return result; +} + +inline void file_lock::unlock_sharable() +{ + if(!ipcdetail::release_file_lock_sharable(m_file_hnd)){ + error_info err(system_error_code()); + throw interprocess_exception(err); + } +} + +} //namespace interprocess { +} //namespace boost { + +#include <boost/interprocess/detail/config_end.hpp> + +#endif //BOOST_INTERPROCESS_FILE_LOCK_HPP diff --git a/boost/interprocess/sync/interprocess_condition.hpp b/boost/interprocess/sync/interprocess_condition.hpp new file mode 100644 index 0000000000..b0e74fc342 --- /dev/null +++ b/boost/interprocess/sync/interprocess_condition.hpp @@ -0,0 +1,172 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2005-2011. 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_CONDITION_HPP +#define BOOST_INTERPROCESS_CONDITION_HPP + +#if (defined _MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif + +/// @cond + +#include <boost/interprocess/detail/config_begin.hpp> +#include <boost/interprocess/detail/workaround.hpp> + +#include <boost/interprocess/detail/posix_time_types_wrk.hpp> +#include <boost/interprocess/sync/interprocess_mutex.hpp> +#include <boost/interprocess/exceptions.hpp> +#include <boost/limits.hpp> +#include <boost/assert.hpp> + +#if !defined(BOOST_INTERPROCESS_FORCE_GENERIC_EMULATION) && defined(BOOST_INTERPROCESS_POSIX_PROCESS_SHARED) + #include <boost/interprocess/sync/posix/condition.hpp> + #define BOOST_INTERPROCESS_USE_POSIX +//Experimental... +//#elif !defined(BOOST_INTERPROCESS_FORCE_GENERIC_EMULATION) && defined (BOOST_INTERPROCESS_WINDOWS) +// #include <boost/interprocess/sync/windows/condition.hpp> +// #define BOOST_INTERPROCESS_USE_WINDOWS +#elif !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED) + #include <boost/interprocess/sync/spin/condition.hpp> + #define BOOST_INTERPROCESS_USE_GENERIC_EMULATION +#endif + +/// @endcond + +//!\file +//!Describes process-shared variables interprocess_condition class + +namespace boost { + +namespace posix_time +{ class ptime; } + +namespace interprocess { + +class named_condition; + +//!This class is a condition variable that can be placed in shared memory or +//!memory mapped files. +class interprocess_condition +{ + /// @cond + //Non-copyable + interprocess_condition(const interprocess_condition &); + interprocess_condition &operator=(const interprocess_condition &); + friend class named_condition; + /// @endcond + public: + //!Constructs a interprocess_condition. On error throws interprocess_exception. + interprocess_condition(){} + + //!Destroys *this + //!liberating system resources. + ~interprocess_condition(){} + + //!If there is a thread waiting on *this, change that + //!thread's state to ready. Otherwise there is no effect. + void notify_one() + { m_condition.notify_one(); } + + //!Change the state of all threads waiting on *this to ready. + //!If there are no waiting threads, notify_all() has no effect. + void notify_all() + { m_condition.notify_all(); } + + //!Releases the lock on the interprocess_mutex object associated with lock, blocks + //!the current thread of execution until readied by a call to + //!this->notify_one() or this->notify_all(), and then reacquires the lock. + template <typename L> + void wait(L& lock) + { + if (!lock) + throw lock_exception(); + this->do_wait(*lock.mutex()); + } + + //!The same as: + //!while (!pred()) wait(lock) + template <typename L, typename Pr> + void wait(L& lock, Pr pred) + { + if (!lock) + throw lock_exception(); + + while (!pred()) + this->do_wait(*lock.mutex()); + } + + //!Releases the lock on the interprocess_mutex object associated with lock, blocks + //!the current thread of execution until readied by a call to + //!this->notify_one() or this->notify_all(), or until time abs_time is reached, + //!and then reacquires the lock. + //!Returns: false if time abs_time is reached, otherwise true. + template <typename L> + bool timed_wait(L& lock, const boost::posix_time::ptime &abs_time) + { + if(abs_time == boost::posix_time::pos_infin){ + this->wait(lock); + return true; + } + if (!lock) + throw lock_exception(); + return this->do_timed_wait(abs_time, *lock.mutex()); + } + + //!The same as: while (!pred()) { + //! if (!timed_wait(lock, abs_time)) return pred(); + //! } return true; + template <typename L, typename Pr> + bool timed_wait(L& lock, const boost::posix_time::ptime &abs_time, Pr pred) + { + if(abs_time == boost::posix_time::pos_infin){ + this->wait(lock, pred); + return true; + } + if (!lock) + throw lock_exception(); + while (!pred()){ + if (!this->do_timed_wait(abs_time, *lock.mutex())) + return pred(); + } + + return true; + } + + /// @cond + + void do_wait(interprocess_mutex &mut) + { m_condition.do_wait(mut.mutex); } + + bool do_timed_wait(const boost::posix_time::ptime &abs_time, interprocess_mutex &mut) + { return m_condition.do_timed_wait(abs_time, mut.mutex); } + + private: + #if defined (BOOST_INTERPROCESS_USE_GENERIC_EMULATION) + #undef BOOST_INTERPROCESS_USE_GENERIC_EMULATION + ipcdetail::spin_condition m_condition; + #elif defined(BOOST_INTERPROCESS_USE_POSIX) + #undef BOOST_INTERPROCESS_USE_POSIX + ipcdetail::posix_condition m_condition; + #elif defined(BOOST_INTERPROCESS_USE_WINDOWS) + #undef BOOST_INTERPROCESS_USE_WINDOWS + ipcdetail::windows_condition m_condition; + #else + #error "Unknown platform for interprocess_mutex" + #endif + /// @endcond +}; + +} //namespace interprocess +} // namespace boost + +#include <boost/interprocess/detail/config_end.hpp> + +#endif // BOOST_INTERPROCESS_CONDITION_HPP diff --git a/boost/interprocess/sync/interprocess_mutex.hpp b/boost/interprocess/sync/interprocess_mutex.hpp new file mode 100644 index 0000000000..478cf78cc3 --- /dev/null +++ b/boost/interprocess/sync/interprocess_mutex.hpp @@ -0,0 +1,170 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2005-2011. 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. +// +////////////////////////////////////////////////////////////////////////////// +// +// Parts of the pthread code come from Boost Threads code. +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef BOOST_INTERPROCESS_MUTEX_HPP +#define BOOST_INTERPROCESS_MUTEX_HPP + +/// @cond + +#if (defined _MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif + +#include <boost/interprocess/detail/config_begin.hpp> +#include <boost/interprocess/exceptions.hpp> +#include <boost/interprocess/detail/workaround.hpp> +#include <boost/interprocess/detail/posix_time_types_wrk.hpp> +#include <boost/assert.hpp> + +#if !defined(BOOST_INTERPROCESS_FORCE_GENERIC_EMULATION) && defined (BOOST_INTERPROCESS_POSIX_PROCESS_SHARED) + #include <boost/interprocess/sync/posix/mutex.hpp> + #define BOOST_INTERPROCESS_USE_POSIX +//Experimental... +//#elif !defined(BOOST_INTERPROCESS_FORCE_GENERIC_EMULATION) && defined (BOOST_INTERPROCESS_WINDOWS) +// #include <boost/interprocess/sync/windows/mutex.hpp> +// #define BOOST_INTERPROCESS_USE_WINDOWS +#elif !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED) + #include <boost/interprocess/sync/spin/mutex.hpp> + #define BOOST_INTERPROCESS_USE_GENERIC_EMULATION + +namespace boost { +namespace interprocess { +namespace ipcdetail{ +namespace robust_emulation_helpers { + +template<class T> +class mutex_traits; + +}}}} + +#endif + +/// @endcond + +//!\file +//!Describes a mutex class that can be placed in memory shared by +//!several processes. + +namespace boost { +namespace interprocess { + +class interprocess_condition; + +//!Wraps a interprocess_mutex that can be placed in shared memory and can be +//!shared between processes. Allows timed lock tries +class interprocess_mutex +{ + /// @cond + //Non-copyable + interprocess_mutex(const interprocess_mutex &); + interprocess_mutex &operator=(const interprocess_mutex &); + friend class interprocess_condition; + /// @endcond + public: + + //!Constructor. + //!Throws interprocess_exception on error. + interprocess_mutex(); + + //!Destructor. If any process uses the mutex after the destructor is called + //!the result is undefined. Does not throw. + ~interprocess_mutex(); + + //!Effects: The calling thread tries to obtain ownership of the mutex, and + //! if another thread has ownership of the mutex, it waits until it can + //! obtain the ownership. If a thread takes ownership of the mutex the + //! mutex must be unlocked by the same mutex. + //!Throws: interprocess_exception on error. + void lock(); + + //!Effects: The calling thread tries to obtain ownership of the mutex, and + //! if another thread has ownership of the mutex returns immediately. + //!Returns: If the thread acquires ownership of the mutex, returns true, if + //! the another thread has ownership of the mutex, returns false. + //!Throws: interprocess_exception on error. + bool try_lock(); + + //!Effects: The calling thread will try to obtain exclusive ownership of the + //! mutex if it can do so in until the specified time is reached. If the + //! mutex supports recursive locking, the mutex must be unlocked the same + //! number of times it is locked. + //!Returns: If the thread acquires ownership of the mutex, returns true, if + //! the timeout expires returns false. + //!Throws: interprocess_exception on error. + bool timed_lock(const boost::posix_time::ptime &abs_time); + + //!Effects: The calling thread releases the exclusive ownership of the mutex. + //!Throws: interprocess_exception on error. + void unlock(); + /// @cond + private: + + #if defined(BOOST_INTERPROCESS_USE_GENERIC_EMULATION) + #undef BOOST_INTERPROCESS_USE_GENERIC_EMULATION + friend class ipcdetail::robust_emulation_helpers::mutex_traits<interprocess_mutex>; + void take_ownership(){ mutex.take_ownership(); } + ipcdetail::spin_mutex mutex; + #elif defined(BOOST_INTERPROCESS_USE_POSIX) + #undef BOOST_INTERPROCESS_USE_POSIX + ipcdetail::posix_mutex mutex; + #elif defined(BOOST_INTERPROCESS_USE_WINDOWS) + #undef BOOST_INTERPROCESS_USE_WINDOWS + ipcdetail::windows_mutex mutex; + #else + #error "Unknown platform for interprocess_mutex" + #endif + /// @endcond +}; + +} //namespace interprocess { +} //namespace boost { + + +namespace boost { +namespace interprocess { + +inline interprocess_mutex::interprocess_mutex(){} + +inline interprocess_mutex::~interprocess_mutex(){} + +inline void interprocess_mutex::lock() +{ + #ifdef BOOST_INTERPROCESS_ENABLE_TIMEOUT_WHEN_LOCKING + boost::posix_time::ptime wait_time + = boost::posix_time::microsec_clock::universal_time() + + boost::posix_time::milliseconds(BOOST_INTERPROCESS_TIMEOUT_WHEN_LOCKING_DURATION_MS); + if (!mutex.timed_lock(wait_time)) + { + throw interprocess_exception(timeout_when_locking_error, "Interprocess mutex timeout when locking. Possible deadlock: owner died without unlocking?"); + } + #else + mutex.lock(); + #endif +} + +inline bool interprocess_mutex::try_lock() +{ return mutex.try_lock(); } + +inline bool interprocess_mutex::timed_lock(const boost::posix_time::ptime &abs_time) +{ return mutex.timed_lock(abs_time); } + +inline void interprocess_mutex::unlock() +{ mutex.unlock(); } + +} //namespace interprocess { +} //namespace boost { + +#include <boost/interprocess/detail/config_end.hpp> + +#endif //BOOST_INTERPROCESS_MUTEX_HPP diff --git a/boost/interprocess/sync/interprocess_recursive_mutex.hpp b/boost/interprocess/sync/interprocess_recursive_mutex.hpp new file mode 100644 index 0000000000..308819ff27 --- /dev/null +++ b/boost/interprocess/sync/interprocess_recursive_mutex.hpp @@ -0,0 +1,177 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2005-2011. 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. +// +////////////////////////////////////////////////////////////////////////////// +// +// Parts of the pthread code come from Boost Threads code: +// +////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2001-2003 +// William E. Kempf +// +// Permission to use, copy, modify, distribute and sell this software +// and its documentation for any purpose is hereby granted without fee, +// provided that the above copyright notice appear in all copies and +// that both that copyright notice and this permission notice appear +// in supporting documentation. William E. Kempf makes no representations +// about the suitability of this software for any purpose. +// It is provided "as is" without express or implied warranty. +////////////////////////////////////////////////////////////////////////////// + +#ifndef BOOST_INTERPROCESS_RECURSIVE_MUTEX_HPP +#define BOOST_INTERPROCESS_RECURSIVE_MUTEX_HPP + +/// @cond + +#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/detail/posix_time_types_wrk.hpp> +#include <boost/assert.hpp> + +#if !defined(BOOST_INTERPROCESS_FORCE_GENERIC_EMULATION) && \ + (defined(BOOST_INTERPROCESS_POSIX_PROCESS_SHARED) && defined (BOOST_INTERPROCESS_POSIX_RECURSIVE_MUTEXES)) + #include <boost/interprocess/sync/posix/recursive_mutex.hpp> + #define BOOST_INTERPROCESS_USE_POSIX +//Experimental... +//#elif !defined(BOOST_INTERPROCESS_FORCE_GENERIC_EMULATION) && defined (BOOST_INTERPROCESS_WINDOWS) +// #include <boost/interprocess/sync/windows/recursive_mutex.hpp> +// #define BOOST_INTERPROCESS_USE_WINDOWS +#elif !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED) + #include <boost/interprocess/sync/spin/recursive_mutex.hpp> + #define BOOST_INTERPROCESS_USE_GENERIC_EMULATION +#endif + +#if defined (BOOST_INTERPROCESS_USE_GENERIC_EMULATION) +namespace boost { +namespace interprocess { +namespace ipcdetail{ +namespace robust_emulation_helpers { + +template<class T> +class mutex_traits; + +}}}} + +#endif + +/// @endcond + +//!\file +//!Describes interprocess_recursive_mutex and shared_recursive_try_mutex classes + +namespace boost { +namespace interprocess { + +//!Wraps a interprocess_mutex that can be placed in shared memory and can be +//!shared between processes. Allows several locking calls by the same +//!process. Allows timed lock tries +class interprocess_recursive_mutex +{ + /// @cond + //Non-copyable + interprocess_recursive_mutex(const interprocess_recursive_mutex &); + interprocess_recursive_mutex &operator=(const interprocess_recursive_mutex &); + /// @endcond + public: + //!Constructor. + //!Throws interprocess_exception on error. + interprocess_recursive_mutex(); + + //!Destructor. If any process uses the mutex after the destructor is called + //!the result is undefined. Does not throw. + ~interprocess_recursive_mutex(); + + //!Effects: The calling thread tries to obtain ownership of the mutex, and + //! if another thread has ownership of the mutex, it waits until it can + //! obtain the ownership. If a thread takes ownership of the mutex the + //! mutex must be unlocked by the same mutex. The mutex must be unlocked + //! the same number of times it is locked. + //!Throws: interprocess_exception on error. + void lock(); + + //!Tries to lock the interprocess_mutex, returns false when interprocess_mutex + //!is already locked, returns true when success. The mutex must be unlocked + //!the same number of times it is locked. + //!Throws: interprocess_exception if a severe error is found + bool try_lock(); + + //!Tries to lock the interprocess_mutex, if interprocess_mutex can't be locked before + //!abs_time time, returns false. The mutex must be unlocked + //! the same number of times it is locked. + //!Throws: interprocess_exception if a severe error is found + bool timed_lock(const boost::posix_time::ptime &abs_time); + + //!Effects: The calling thread releases the exclusive ownership of the mutex. + //! If the mutex supports recursive locking, the mutex must be unlocked the + //! same number of times it is locked. + //!Throws: interprocess_exception on error. + void unlock(); + /// @cond + private: + + #if defined (BOOST_INTERPROCESS_USE_GENERIC_EMULATION) + #undef BOOST_INTERPROCESS_USE_GENERIC_EMULATION + void take_ownership(){ mutex.take_ownership(); } + friend class ipcdetail::robust_emulation_helpers::mutex_traits<interprocess_recursive_mutex>; + ipcdetail::spin_recursive_mutex mutex; + #elif defined(BOOST_INTERPROCESS_USE_POSIX) + #undef BOOST_INTERPROCESS_USE_POSIX + ipcdetail::posix_recursive_mutex mutex; + #elif defined(BOOST_INTERPROCESS_USE_WINDOWS) + #undef BOOST_INTERPROCESS_USE_WINDOWS + ipcdetail::windows_recursive_mutex mutex; + #else + #error "Unknown platform for interprocess_mutex" + #endif + /// @endcond +}; + +} //namespace interprocess { +} //namespace boost { + +namespace boost { +namespace interprocess { + +inline interprocess_recursive_mutex::interprocess_recursive_mutex(){} + +inline interprocess_recursive_mutex::~interprocess_recursive_mutex(){} + +inline void interprocess_recursive_mutex::lock() +{ + #ifdef BOOST_INTERPROCESS_ENABLE_TIMEOUT_WHEN_LOCKING + boost::posix_time::ptime wait_time + = boost::posix_time::microsec_clock::universal_time() + + boost::posix_time::milliseconds(BOOST_INTERPROCESS_TIMEOUT_WHEN_LOCKING_DURATION_MS); + if (!mutex.timed_lock(wait_time)){ + throw interprocess_exception(timeout_when_locking_error, "Interprocess mutex timeout when locking. Possible deadlock: owner died without unlocking?"); + } + #else + mutex.lock(); + #endif +} + +inline bool interprocess_recursive_mutex::try_lock() +{ return mutex.try_lock(); } + +inline bool interprocess_recursive_mutex::timed_lock(const boost::posix_time::ptime &abs_time) +{ return mutex.timed_lock(abs_time); } + +inline void interprocess_recursive_mutex::unlock() +{ mutex.unlock(); } + +} //namespace interprocess { +} //namespace boost { + +#include <boost/interprocess/detail/config_end.hpp> + +#endif //BOOST_INTERPROCESS_RECURSIVE_MUTEX_HPP diff --git a/boost/interprocess/sync/interprocess_semaphore.hpp b/boost/interprocess/sync/interprocess_semaphore.hpp new file mode 100644 index 0000000000..4ffcdcf691 --- /dev/null +++ b/boost/interprocess/sync/interprocess_semaphore.hpp @@ -0,0 +1,145 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2005-2011. 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_SEMAPHORE_HPP +#define BOOST_INTERPROCESS_SEMAPHORE_HPP + +/// @cond + +#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/creation_tags.hpp> +#include <boost/interprocess/exceptions.hpp> +#include <boost/interprocess/detail/posix_time_types_wrk.hpp> + +#if !defined(BOOST_INTERPROCESS_FORCE_GENERIC_EMULATION) && \ + (defined(BOOST_INTERPROCESS_POSIX_PROCESS_SHARED) && defined(BOOST_INTERPROCESS_POSIX_NAMED_SEMAPHORES)) + #include <boost/interprocess/sync/posix/semaphore.hpp> + #define BOOST_INTERPROCESS_USE_POSIX +//Experimental... +//#elif !defined(BOOST_INTERPROCESS_FORCE_GENERIC_EMULATION) && defined (BOOST_INTERPROCESS_WINDOWS) +// #include <boost/interprocess/sync/windows/semaphore.hpp> +// #define BOOST_INTERPROCESS_USE_WINDOWS +#elif !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED) + #include <boost/interprocess/sync/spin/semaphore.hpp> + #define BOOST_INTERPROCESS_USE_GENERIC_EMULATION +#endif + +/// @endcond + +//!\file +//!Describes a interprocess_semaphore class for inter-process synchronization + +namespace boost { +namespace interprocess { + +//!Wraps a interprocess_semaphore that can be placed in shared memory and can be +//!shared between processes. Allows timed lock tries +class interprocess_semaphore +{ + /// @cond + //Non-copyable + interprocess_semaphore(const interprocess_semaphore &); + interprocess_semaphore &operator=(const interprocess_semaphore &); + /// @endcond + public: + //!Creates a interprocess_semaphore with the given initial count. + //!interprocess_exception if there is an error.*/ + interprocess_semaphore(unsigned int initialCount); + + //!Destroys the interprocess_semaphore. + //!Does not throw + ~interprocess_semaphore(); + + //!Increments the interprocess_semaphore count. If there are processes/threads blocked waiting + //!for the interprocess_semaphore, then one of these processes will return successfully from + //!its wait function. If there is an error an interprocess_exception exception is thrown. + void post(); + + //!Decrements the interprocess_semaphore. If the interprocess_semaphore value is not greater than zero, + //!then the calling process/thread blocks until it can decrement the counter. + //!If there is an error an interprocess_exception exception is thrown. + void wait(); + + //!Decrements the interprocess_semaphore if the interprocess_semaphore's value is greater than zero + //!and returns true. If the value is not greater than zero returns false. + //!If there is an error an interprocess_exception exception is thrown. + bool try_wait(); + + //!Decrements the interprocess_semaphore if the interprocess_semaphore's value is greater + //!than zero and returns true. Otherwise, waits for the interprocess_semaphore + //!to the posted or the timeout expires. If the timeout expires, the + //!function returns false. If the interprocess_semaphore is posted the function + //!returns true. If there is an error throws sem_exception + bool timed_wait(const boost::posix_time::ptime &abs_time); + + //!Returns the interprocess_semaphore count +// int get_count() const; + /// @cond + private: + #if defined(BOOST_INTERPROCESS_USE_GENERIC_EMULATION) + #undef BOOST_INTERPROCESS_USE_GENERIC_EMULATION + ipcdetail::spin_semaphore m_sem; + #elif defined(BOOST_INTERPROCESS_USE_WINDOWS) + #undef BOOST_INTERPROCESS_USE_WINDOWS + ipcdetail::windows_semaphore m_sem; + #else + #undef BOOST_INTERPROCESS_USE_POSIX + ipcdetail::posix_semaphore m_sem; + #endif //#if defined(BOOST_INTERPROCESS_USE_GENERIC_EMULATION) + /// @endcond +}; + +} //namespace interprocess { +} //namespace boost { + +namespace boost { +namespace interprocess { + +inline interprocess_semaphore::interprocess_semaphore(unsigned int initialCount) + : m_sem(initialCount) +{} + +inline interprocess_semaphore::~interprocess_semaphore(){} + +inline void interprocess_semaphore::wait() +{ + #ifdef BOOST_INTERPROCESS_ENABLE_TIMEOUT_WHEN_LOCKING + boost::posix_time::ptime wait_time + = boost::posix_time::microsec_clock::universal_time() + + boost::posix_time::milliseconds(BOOST_INTERPROCESS_TIMEOUT_WHEN_LOCKING_DURATION_MS); + if (!m_sem.timed_wait(wait_time)) + { + throw interprocess_exception(timeout_when_waiting_error, "Interprocess semaphore timeout when waiting. Possible deadlock: owner died without posting?"); + } + #else + m_sem.wait(); + #endif +} +inline bool interprocess_semaphore::try_wait() +{ return m_sem.try_wait(); } + +inline bool interprocess_semaphore::timed_wait(const boost::posix_time::ptime &abs_time) +{ return m_sem.timed_wait(abs_time); } + +inline void interprocess_semaphore::post() +{ m_sem.post(); } + +} //namespace interprocess { +} //namespace boost { + +#include <boost/interprocess/detail/config_end.hpp> + +#endif //BOOST_INTERPROCESS_SEMAPHORE_HPP diff --git a/boost/interprocess/sync/interprocess_upgradable_mutex.hpp b/boost/interprocess/sync/interprocess_upgradable_mutex.hpp new file mode 100644 index 0000000000..048407f4b3 --- /dev/null +++ b/boost/interprocess/sync/interprocess_upgradable_mutex.hpp @@ -0,0 +1,659 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2005-2011. 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/detail/posix_time_types_wrk.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 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 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 exclusive 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 exclusive + //! 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 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 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 diff --git a/boost/interprocess/sync/lock_options.hpp b/boost/interprocess/sync/lock_options.hpp new file mode 100644 index 0000000000..74f3399a4c --- /dev/null +++ b/boost/interprocess/sync/lock_options.hpp @@ -0,0 +1,55 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2005-2011. 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_LOCK_OPTIONS_HPP +#define BOOST_INTERPROCESS_LOCK_OPTIONS_HPP + +#if (defined _MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif + +#include <boost/interprocess/detail/config_begin.hpp> +#include <boost/interprocess/detail/workaround.hpp> + +//!\file +//!Describes the lock options with associated with interprocess_mutex lock constructors. + +namespace boost { + +namespace posix_time +{ class ptime; } + +namespace interprocess { + +//!Type to indicate to a mutex lock constructor that must not lock the mutex. +struct defer_lock_type{}; +//!Type to indicate to a mutex lock constructor that must try to lock the mutex. +struct try_to_lock_type {}; +//!Type to indicate to a mutex lock constructor that the mutex is already locked. +struct accept_ownership_type{}; + +//!An object indicating that the locking +//!must be deferred. +static const defer_lock_type defer_lock = defer_lock_type(); + +//!An object indicating that a try_lock() +//!operation must be executed. +static const try_to_lock_type try_to_lock = try_to_lock_type(); + +//!An object indicating that the ownership of lockable +//!object must be accepted by the new owner. +static const accept_ownership_type accept_ownership = accept_ownership_type(); + +} // namespace interprocess { +} // namespace boost{ + +#include <boost/interprocess/detail/config_end.hpp> + +#endif // BOOST_INTERPROCESS_LOCK_OPTIONS_HPP diff --git a/boost/interprocess/sync/mutex_family.hpp b/boost/interprocess/sync/mutex_family.hpp new file mode 100644 index 0000000000..a9215e24fe --- /dev/null +++ b/boost/interprocess/sync/mutex_family.hpp @@ -0,0 +1,56 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2005-2011. 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_MUTEX_FAMILY_HPP +#define BOOST_INTERPROCESS_MUTEX_FAMILY_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/interprocess_mutex.hpp> +#include <boost/interprocess/sync/interprocess_recursive_mutex.hpp> +#include <boost/interprocess/sync/null_mutex.hpp> + +//!\file +//!Describes a shared interprocess_mutex family fit algorithm used to allocate objects in shared memory. + +namespace boost { + +namespace interprocess { + +//!Describes interprocess_mutex family to use with Interprocess framework +//!based on boost::interprocess synchronization objects. +struct mutex_family +{ + typedef boost::interprocess::interprocess_mutex mutex_type; + typedef boost::interprocess::interprocess_recursive_mutex recursive_mutex_type; +}; + +//!Describes interprocess_mutex family to use with Interprocess frameworks +//!based on null operation synchronization objects. +struct null_mutex_family +{ + typedef boost::interprocess::null_mutex mutex_type; + typedef boost::interprocess::null_mutex recursive_mutex_type; +}; + +} //namespace interprocess { + +} //namespace boost { + +#include <boost/interprocess/detail/config_end.hpp> + +#endif //#ifndef BOOST_INTERPROCESS_MUTEX_FAMILY_HPP + + diff --git a/boost/interprocess/sync/named_condition.hpp b/boost/interprocess/sync/named_condition.hpp new file mode 100644 index 0000000000..c5529eda6c --- /dev/null +++ b/boost/interprocess/sync/named_condition.hpp @@ -0,0 +1,172 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2005-2011. 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_NAMED_CONDITION_HPP +#define BOOST_INTERPROCESS_NAMED_CONDITION_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/creation_tags.hpp> +#include <boost/interprocess/exceptions.hpp> +#include <boost/interprocess/detail/interprocess_tester.hpp> +#include <boost/interprocess/permissions.hpp> +#include <boost/interprocess/detail/posix_time_types_wrk.hpp> +#include <boost/interprocess/sync/shm/named_condition.hpp> + +//!\file +//!Describes a named condition class for inter-process synchronization + +namespace boost { +namespace interprocess { + +/// @cond +namespace ipcdetail{ class interprocess_tester; } +/// @endcond + +//! A global condition variable that can be created by name. +//! This condition variable is designed to work with named_mutex and +//! can't be placed in shared memory or memory mapped files. +class named_condition +{ + /// @cond + //Non-copyable + named_condition(); + named_condition(const named_condition &); + named_condition &operator=(const named_condition &); + /// @endcond + public: + //!Creates a global condition with a name. + //!If the condition can't be created throws interprocess_exception + named_condition(create_only_t create_only, const char *name, const permissions &perm = permissions()); + + //!Opens or creates a global condition with a name. + //!If the condition is created, this call is equivalent to + //!named_condition(create_only_t, ... ) + //!If the condition is already created, this call is equivalent + //!named_condition(open_only_t, ... ) + //!Does not throw + named_condition(open_or_create_t open_or_create, const char *name, const permissions &perm = permissions()); + + //!Opens a global condition with a name if that condition is previously + //!created. If it is not previously created this function throws + //!interprocess_exception. + named_condition(open_only_t open_only, const char *name); + + //!Destroys *this and indicates that the calling process is finished using + //!the resource. The destructor function will deallocate + //!any system resources allocated by the system for use by this process for + //!this resource. The resource can still be opened again calling + //!the open constructor overload. To erase the resource from the system + //!use remove(). + ~named_condition(); + + //!If there is a thread waiting on *this, change that + //!thread's state to ready. Otherwise there is no effect.*/ + void notify_one(); + + //!Change the state of all threads waiting on *this to ready. + //!If there are no waiting threads, notify_all() has no effect. + void notify_all(); + + //!Releases the lock on the named_mutex object associated with lock, blocks + //!the current thread of execution until readied by a call to + //!this->notify_one() or this->notify_all(), and then reacquires the lock. + template <typename L> + void wait(L& lock); + + //!The same as: + //!while (!pred()) wait(lock) + template <typename L, typename Pr> + void wait(L& lock, Pr pred); + + //!Releases the lock on the named_mutex object associated with lock, blocks + //!the current thread of execution until readied by a call to + //!this->notify_one() or this->notify_all(), or until time abs_time is reached, + //!and then reacquires the lock. + //!Returns: false if time abs_time is reached, otherwise true. + template <typename L> + bool timed_wait(L& lock, const boost::posix_time::ptime &abs_time); + + //!The same as: while (!pred()) { + //! if (!timed_wait(lock, abs_time)) return pred(); + //! } return true; + template <typename L, typename Pr> + bool timed_wait(L& lock, const boost::posix_time::ptime &abs_time, Pr pred); + + //!Erases a named condition from the system. + //!Returns false on error. Never throws. + static bool remove(const char *name); + + /// @cond + private: + ipcdetail::shm_named_condition m_cond; + + friend class ipcdetail::interprocess_tester; + void dont_close_on_destruction() + { ipcdetail::interprocess_tester::dont_close_on_destruction(m_cond); } + /// @endcond +}; + +/// @cond + +inline named_condition::~named_condition() +{} + +inline named_condition::named_condition(create_only_t, const char *name, const permissions &perm) + : m_cond(create_only_t(), name, perm) +{} + +inline named_condition::named_condition(open_or_create_t, const char *name, const permissions &perm) + : m_cond(open_or_create_t(), name, perm) +{} + +inline named_condition::named_condition(open_only_t, const char *name) + : m_cond(open_only_t(), name) +{} + +inline void named_condition::notify_one() +{ m_cond.notify_one(); } + +inline void named_condition::notify_all() +{ m_cond.notify_all(); } + +template <typename L> +inline void named_condition::wait(L& lock) +{ m_cond.wait(lock); } + +template <typename L, typename Pr> +inline void named_condition::wait(L& lock, Pr pred) +{ m_cond.wait(lock, pred); } + +template <typename L> +inline bool named_condition::timed_wait + (L& lock, const boost::posix_time::ptime &abs_time) +{ return m_cond.timed_wait(lock, abs_time); } + +template <typename L, typename Pr> +inline bool named_condition::timed_wait + (L& lock, const boost::posix_time::ptime &abs_time, Pr pred) +{ return m_cond.timed_wait(lock, abs_time, pred); } + +inline bool named_condition::remove(const char *name) +{ return ipcdetail::shm_named_condition::remove(name); } + +/// @endcond + +} //namespace interprocess +} //namespace boost + +#include <boost/interprocess/detail/config_end.hpp> + +#endif // BOOST_INTERPROCESS_NAMED_CONDITION_HPP diff --git a/boost/interprocess/sync/named_mutex.hpp b/boost/interprocess/sync/named_mutex.hpp new file mode 100644 index 0000000000..3e56b81794 --- /dev/null +++ b/boost/interprocess/sync/named_mutex.hpp @@ -0,0 +1,163 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2005-2011. 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_NAMED_MUTEX_HPP +#define BOOST_INTERPROCESS_NAMED_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/creation_tags.hpp> +#include <boost/interprocess/exceptions.hpp> +#include <boost/interprocess/detail/interprocess_tester.hpp> +#include <boost/interprocess/detail/posix_time_types_wrk.hpp> +#include <boost/interprocess/permissions.hpp> + +#if defined(BOOST_INTERPROCESS_NAMED_MUTEX_USES_POSIX_SEMAPHORES) +#include <boost/interprocess/sync/posix/named_mutex.hpp> +#else +#include <boost/interprocess/sync/shm/named_mutex.hpp> +#endif + +//!\file +//!Describes a named mutex class for inter-process synchronization + +namespace boost { +namespace interprocess { + +class named_condition; + +//!A mutex with a global name, so it can be found from different +//!processes. This mutex can't be placed in shared memory, and +//!each process should have it's own named_mutex. +class named_mutex +{ + /// @cond + + //Non-copyable + named_mutex(); + named_mutex(const named_mutex &); + named_mutex &operator=(const named_mutex &); + friend class named_condition; + /// @endcond + + public: + //!Creates a global interprocess_mutex with a name. + //!Throws interprocess_exception on error. + named_mutex(create_only_t create_only, const char *name, const permissions &perm = permissions()); + + //!Opens or creates a global mutex with a name. + //!If the mutex is created, this call is equivalent to + //!named_mutex(create_only_t, ... ) + //!If the mutex is already created, this call is equivalent + //!named_mutex(open_only_t, ... ) + //!Does not throw + named_mutex(open_or_create_t open_or_create, const char *name, const permissions &perm = permissions()); + + //!Opens a global mutex with a name if that mutex is previously + //!created. If it is not previously created this function throws + //!interprocess_exception. + named_mutex(open_only_t open_only, const char *name); + + //!Destroys *this and indicates that the calling process is finished using + //!the resource. The destructor function will deallocate + //!any system resources allocated by the system for use by this process for + //!this resource. The resource can still be opened again calling + //!the open constructor overload. To erase the resource from the system + //!use remove(). + ~named_mutex(); + + //!Unlocks a previously locked + //!interprocess_mutex. + void unlock(); + + //!Locks interprocess_mutex, sleeps when interprocess_mutex is already locked. + //!Throws interprocess_exception if a severe error is found + void lock(); + + //!Tries to lock the interprocess_mutex, returns false when interprocess_mutex + //!is already locked, returns true when success. + //!Throws interprocess_exception if a severe error is found + bool try_lock(); + + //!Tries to lock the interprocess_mutex until time abs_time, + //!Returns false when timeout expires, returns true when locks. + //!Throws interprocess_exception if a severe error is found + bool timed_lock(const boost::posix_time::ptime &abs_time); + + //!Erases a named mutex from the system. + //!Returns false on error. Never throws. + static bool remove(const char *name); + + /// @cond + private: + friend class ipcdetail::interprocess_tester; + void dont_close_on_destruction(); + + #if defined(BOOST_INTERPROCESS_NAMED_MUTEX_USES_POSIX_SEMAPHORES) + typedef ipcdetail::posix_named_mutex impl_t; + impl_t m_mut; + #else + typedef ipcdetail::shm_named_mutex impl_t; + impl_t m_mut; + public: + interprocess_mutex *mutex() const + { return m_mut.mutex(); } + #endif + + /// @endcond +}; + +/// @cond + +inline named_mutex::named_mutex(create_only_t, const char *name, const permissions &perm) + : m_mut(create_only_t(), name, perm) +{} + +inline named_mutex::named_mutex(open_or_create_t, const char *name, const permissions &perm) + : m_mut(open_or_create_t(), name, perm) +{} + +inline named_mutex::named_mutex(open_only_t, const char *name) + : m_mut(open_only_t(), name) +{} + +inline void named_mutex::dont_close_on_destruction() +{ ipcdetail::interprocess_tester::dont_close_on_destruction(m_mut); } + +inline named_mutex::~named_mutex() +{} + +inline void named_mutex::lock() +{ m_mut.lock(); } + +inline void named_mutex::unlock() +{ m_mut.unlock(); } + +inline bool named_mutex::try_lock() +{ return m_mut.try_lock(); } + +inline bool named_mutex::timed_lock(const boost::posix_time::ptime &abs_time) +{ return m_mut.timed_lock(abs_time); } + +inline bool named_mutex::remove(const char *name) +{ return impl_t::remove(name); } + +/// @endcond + +} //namespace interprocess { +} //namespace boost { + +#include <boost/interprocess/detail/config_end.hpp> + +#endif //BOOST_INTERPROCESS_NAMED_MUTEX_HPP diff --git a/boost/interprocess/sync/named_recursive_mutex.hpp b/boost/interprocess/sync/named_recursive_mutex.hpp new file mode 100644 index 0000000000..28768cea6f --- /dev/null +++ b/boost/interprocess/sync/named_recursive_mutex.hpp @@ -0,0 +1,154 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2005-2011. 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_NAMED_RECURSIVE_MUTEX_HPP +#define BOOST_INTERPROCESS_NAMED_RECURSIVE_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/creation_tags.hpp> +#include <boost/interprocess/detail/posix_time_types_wrk.hpp> +#include <boost/interprocess/permissions.hpp> +#include <boost/interprocess/sync/shm/named_recursive_mutex.hpp> + +//!\file +//!Describes a named named_recursive_mutex class for inter-process synchronization + +namespace boost { +namespace interprocess { + +/// @cond +namespace ipcdetail{ class interprocess_tester; } +/// @endcond + +//!A recursive mutex with a global name, so it can be found from different +//!processes. This mutex can't be placed in shared memory, and +//!each process should have it's own named_recursive_mutex. +class named_recursive_mutex +{ + /// @cond + //Non-copyable + named_recursive_mutex(); + named_recursive_mutex(const named_recursive_mutex &); + named_recursive_mutex &operator=(const named_recursive_mutex &); + /// @endcond + public: + + //!Creates a global recursive_mutex with a name. + //!If the recursive_mutex can't be created throws interprocess_exception + named_recursive_mutex(create_only_t create_only, const char *name, const permissions &perm = permissions()); + + //!Opens or creates a global recursive_mutex with a name. + //!If the recursive_mutex is created, this call is equivalent to + //!named_recursive_mutex(create_only_t, ... ) + //!If the recursive_mutex is already created, this call is equivalent + //!named_recursive_mutex(open_only_t, ... ) + //!Does not throw + named_recursive_mutex(open_or_create_t open_or_create, const char *name, const permissions &perm = permissions()); + + //!Opens a global recursive_mutex with a name if that recursive_mutex is previously + //!created. If it is not previously created this function throws + //!interprocess_exception. + named_recursive_mutex(open_only_t open_only, const char *name); + + //!Destroys *this and indicates that the calling process is finished using + //!the resource. The destructor function will deallocate + //!any system resources allocated by the system for use by this process for + //!this resource. The resource can still be opened again calling + //!the open constructor overload. To erase the resource from the system + //!use remove(). + ~named_recursive_mutex(); + + //!Unlocks a previously locked + //!named_recursive_mutex. + void unlock(); + + //!Locks named_recursive_mutex, sleeps when named_recursive_mutex is already locked. + //!Throws interprocess_exception if a severe error is found. + void lock(); + + //!Tries to lock the named_recursive_mutex, returns false when named_recursive_mutex + //!is already locked, returns true when success. + //!Throws interprocess_exception if a severe error is found. + bool try_lock(); + + //!Tries to lock the named_recursive_mutex until time abs_time, + //!Returns false when timeout expires, returns true when locks. + //!Throws interprocess_exception if a severe error is found + bool timed_lock(const boost::posix_time::ptime &abs_time); + + //!Erases a named recursive mutex + //!from the system + static bool remove(const char *name); + + /// @cond + private: + friend class ipcdetail::interprocess_tester; + void dont_close_on_destruction(); + + typedef ipcdetail::shm_named_recursive_mutex impl_t; + impl_t m_mut; + + /// @endcond +}; + +/// @cond + +inline named_recursive_mutex::~named_recursive_mutex() +{} + +inline void named_recursive_mutex::dont_close_on_destruction() +{ ipcdetail::interprocess_tester::dont_close_on_destruction(m_mut); } + +inline named_recursive_mutex::named_recursive_mutex(create_only_t, const char *name, const permissions &perm) + : m_mut (create_only, name, perm) +{} + +inline named_recursive_mutex::named_recursive_mutex(open_or_create_t, const char *name, const permissions &perm) + : m_mut (open_or_create, name, perm) +{} + +inline named_recursive_mutex::named_recursive_mutex(open_only_t, const char *name) + : m_mut (open_only, name) +{} + +inline void named_recursive_mutex::lock() +{ m_mut.lock(); } + +inline void named_recursive_mutex::unlock() +{ m_mut.unlock(); } + +inline bool named_recursive_mutex::try_lock() +{ return m_mut.try_lock(); } + +inline bool named_recursive_mutex::timed_lock(const boost::posix_time::ptime &abs_time) +{ + if(abs_time == boost::posix_time::pos_infin){ + this->lock(); + return true; + } + return m_mut.timed_lock(abs_time); +} + +inline bool named_recursive_mutex::remove(const char *name) +{ return impl_t::remove(name); } + +/// @endcond + +} //namespace interprocess { +} //namespace boost { + +#include <boost/interprocess/detail/config_end.hpp> + +#endif //BOOST_INTERPROCESS_NAMED_RECURSIVE_MUTEX_HPP diff --git a/boost/interprocess/sync/named_semaphore.hpp b/boost/interprocess/sync/named_semaphore.hpp new file mode 100644 index 0000000000..33e06964f9 --- /dev/null +++ b/boost/interprocess/sync/named_semaphore.hpp @@ -0,0 +1,168 @@ + ////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2005-2011. 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_NAMED_SEMAPHORE_HPP +#define BOOST_INTERPROCESS_NAMED_SEMAPHORE_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/creation_tags.hpp> +#include <boost/interprocess/exceptions.hpp> +#include <boost/interprocess/permissions.hpp> +#include <boost/interprocess/detail/interprocess_tester.hpp> +#include <boost/interprocess/detail/posix_time_types_wrk.hpp> + +#if defined(BOOST_INTERPROCESS_NAMED_SEMAPHORE_USES_POSIX_SEMAPHORES) +#include <boost/interprocess/sync/posix/named_semaphore.hpp> +#else +#include <boost/interprocess/sync/shm/named_semaphore.hpp> +#endif + +//!\file +//!Describes a named semaphore class for inter-process synchronization + +namespace boost { +namespace interprocess { + +//!A semaphore with a global name, so it can be found from different +//!processes. Allows several resource sharing patterns and efficient +//!acknowledgment mechanisms. +class named_semaphore +{ + /// @cond + + //Non-copyable + named_semaphore(); + named_semaphore(const named_semaphore &); + named_semaphore &operator=(const named_semaphore &); + /// @endcond + + public: + //!Creates a global semaphore with a name, and an initial count. + //!If the semaphore can't be created throws interprocess_exception + named_semaphore(create_only_t, const char *name, unsigned int initialCount, const permissions &perm = permissions()); + + //!Opens or creates a global semaphore with a name, and an initial count. + //!If the semaphore is created, this call is equivalent to + //!named_semaphore(create_only_t, ...) + //!If the semaphore is already created, this call is equivalent to + //!named_semaphore(open_only_t, ... ) + //!and initialCount is ignored. + named_semaphore(open_or_create_t, const char *name, unsigned int initialCount, const permissions &perm = permissions()); + + //!Opens a global semaphore with a name if that semaphore is previously. + //!created. If it is not previously created this function throws + //!interprocess_exception. + named_semaphore(open_only_t, const char *name); + + //!Destroys *this and indicates that the calling process is finished using + //!the resource. The destructor function will deallocate + //!any system resources allocated by the system for use by this process for + //!this resource. The resource can still be opened again calling + //!the open constructor overload. To erase the resource from the system + //!use remove(). + ~named_semaphore(); + + //!Increments the semaphore count. If there are processes/threads blocked waiting + //!for the semaphore, then one of these processes will return successfully from + //!its wait function. If there is an error an interprocess_exception exception is thrown. + void post(); + + //!Decrements the semaphore. If the semaphore value is not greater than zero, + //!then the calling process/thread blocks until it can decrement the counter. + //!If there is an error an interprocess_exception exception is thrown. + void wait(); + + //!Decrements the semaphore if the semaphore's value is greater than zero + //!and returns true. If the value is not greater than zero returns false. + //!If there is an error an interprocess_exception exception is thrown. + bool try_wait(); + + //!Decrements the semaphore if the semaphore's value is greater + //!than zero and returns true. Otherwise, waits for the semaphore + //!to the posted or the timeout expires. If the timeout expires, the + //!function returns false. If the semaphore is posted the function + //!returns true. If there is an error throws sem_exception + bool timed_wait(const boost::posix_time::ptime &abs_time); + + //!Erases a named semaphore from the system. + //!Returns false on error. Never throws. + static bool remove(const char *name); + + /// @cond + private: + friend class ipcdetail::interprocess_tester; + void dont_close_on_destruction(); + + #if defined(BOOST_INTERPROCESS_NAMED_SEMAPHORE_USES_POSIX_SEMAPHORES) + typedef ipcdetail::posix_named_semaphore impl_t; + #else + typedef ipcdetail::shm_named_semaphore impl_t; + #endif + impl_t m_sem; + /// @endcond +}; + +/// @cond + +inline named_semaphore::named_semaphore + (create_only_t, const char *name, unsigned int initialCount, const permissions &perm) + : m_sem(create_only, name, initialCount, perm) +{} + +inline named_semaphore::named_semaphore + (open_or_create_t, const char *name, unsigned int initialCount, const permissions &perm) + : m_sem(open_or_create, name, initialCount, perm) +{} + +inline named_semaphore::named_semaphore(open_only_t, const char *name) + : m_sem(open_only, name) +{} + +inline named_semaphore::~named_semaphore() +{} + +inline void named_semaphore::dont_close_on_destruction() +{ ipcdetail::interprocess_tester::dont_close_on_destruction(m_sem); } + +inline void named_semaphore::wait() +{ m_sem.wait(); } + +inline void named_semaphore::post() +{ m_sem.post(); } + +inline bool named_semaphore::try_wait() +{ return m_sem.try_wait(); } + +inline bool named_semaphore::timed_wait(const boost::posix_time::ptime &abs_time) +{ + if(abs_time == boost::posix_time::pos_infin){ + this->wait(); + return true; + } + return m_sem.timed_wait(abs_time); +} + +inline bool named_semaphore::remove(const char *name) +{ return impl_t::remove(name); } + +/// @endcond + +} //namespace interprocess { +} //namespace boost { + + +#include <boost/interprocess/detail/config_end.hpp> + +#endif //BOOST_INTERPROCESS_NAMED_SEMAPHORE_HPP diff --git a/boost/interprocess/sync/named_upgradable_mutex.hpp b/boost/interprocess/sync/named_upgradable_mutex.hpp new file mode 100644 index 0000000000..61c6bb4086 --- /dev/null +++ b/boost/interprocess/sync/named_upgradable_mutex.hpp @@ -0,0 +1,372 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2005-2011. 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_named_upgradable_mutex_HPP +#define BOOST_INTERPROCESS_named_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/creation_tags.hpp> +#include <boost/interprocess/exceptions.hpp> +#include <boost/interprocess/shared_memory_object.hpp> +#include <boost/interprocess/detail/managed_open_or_create_impl.hpp> +#include <boost/interprocess/sync/interprocess_upgradable_mutex.hpp> +#include <boost/interprocess/detail/posix_time_types_wrk.hpp> +#include <boost/interprocess/sync/shm/named_creation_functor.hpp> +#include <boost/interprocess/permissions.hpp> + +//!\file +//!Describes a named upgradable mutex class for inter-process synchronization + +namespace boost { +namespace interprocess { + +/// @cond +namespace ipcdetail{ class interprocess_tester; } +/// @endcond + +class named_condition; + +//!A upgradable mutex with a global name, so it can be found from different +//!processes. This mutex can't be placed in shared memory, and +//!each process should have it's own named upgradable mutex. +class named_upgradable_mutex +{ + /// @cond + //Non-copyable + named_upgradable_mutex(); + named_upgradable_mutex(const named_upgradable_mutex &); + named_upgradable_mutex &operator=(const named_upgradable_mutex &); + friend class named_condition; + /// @endcond + public: + + //!Creates a global upgradable mutex with a name. + //!If the upgradable mutex can't be created throws interprocess_exception + named_upgradable_mutex(create_only_t create_only, const char *name, const permissions &perm = permissions()); + + //!Opens or creates a global upgradable mutex with a name, and an initial count. + //!If the upgradable mutex is created, this call is equivalent to + //!named_upgradable_mutex(create_only_t, ...) + //!If the upgradable mutex is already created, this call is equivalent to + //!named_upgradable_mutex(open_only_t, ... ). + named_upgradable_mutex(open_or_create_t open_or_create, const char *name, const permissions &perm = permissions()); + + //!Opens a global upgradable mutex with a name if that upgradable mutex + //!is previously. + //!created. If it is not previously created this function throws + //!interprocess_exception. + named_upgradable_mutex(open_only_t open_only, const char *name); + + //!Destroys *this and indicates that the calling process is finished using + //!the resource. The destructor function will deallocate + //!any system resources allocated by the system for use by this process for + //!this resource. The resource can still be opened again calling + //!the open constructor overload. To erase the resource from the system + //!use remove(). + ~named_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 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 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 exclusive 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 exclusive + //! 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 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 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 it. + //!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(); + + bool try_unlock_sharable_and_lock_upgradable(); + + //!Erases a named upgradable mutex from the system. + //!Returns false on error. Never throws. + static bool remove(const char *name); + + /// @cond + private: + friend class ipcdetail::interprocess_tester; + void dont_close_on_destruction(); + + interprocess_upgradable_mutex *mutex() const + { return static_cast<interprocess_upgradable_mutex*>(m_shmem.get_user_address()); } + + ipcdetail::managed_open_or_create_impl<shared_memory_object> m_shmem; + typedef ipcdetail::named_creation_functor<interprocess_upgradable_mutex> construct_func_t; + /// @endcond +}; + +/// @cond + +inline named_upgradable_mutex::~named_upgradable_mutex() +{} + +inline named_upgradable_mutex::named_upgradable_mutex + (create_only_t, const char *name, const permissions &perm) + : m_shmem (create_only + ,name + ,sizeof(interprocess_upgradable_mutex) + + ipcdetail::managed_open_or_create_impl<shared_memory_object>:: + ManagedOpenOrCreateUserOffset + ,read_write + ,0 + ,construct_func_t(ipcdetail::DoCreate) + ,perm) +{} + +inline named_upgradable_mutex::named_upgradable_mutex + (open_or_create_t, const char *name, const permissions &perm) + : m_shmem (open_or_create + ,name + ,sizeof(interprocess_upgradable_mutex) + + ipcdetail::managed_open_or_create_impl<shared_memory_object>:: + ManagedOpenOrCreateUserOffset + ,read_write + ,0 + ,construct_func_t(ipcdetail::DoOpenOrCreate) + ,perm) +{} + +inline named_upgradable_mutex::named_upgradable_mutex + (open_only_t, const char *name) + : m_shmem (open_only + ,name + ,read_write + ,0 + ,construct_func_t(ipcdetail::DoOpen)) +{} + +inline void named_upgradable_mutex::dont_close_on_destruction() +{ ipcdetail::interprocess_tester::dont_close_on_destruction(m_shmem); } + +inline void named_upgradable_mutex::lock() +{ this->mutex()->lock(); } + +inline void named_upgradable_mutex::unlock() +{ this->mutex()->unlock(); } + +inline bool named_upgradable_mutex::try_lock() +{ return this->mutex()->try_lock(); } + +inline bool named_upgradable_mutex::timed_lock + (const boost::posix_time::ptime &abs_time) +{ + if(abs_time == boost::posix_time::pos_infin){ + this->lock(); + return true; + } + return this->mutex()->timed_lock(abs_time); +} + +inline void named_upgradable_mutex::lock_upgradable() +{ this->mutex()->lock_upgradable(); } + +inline void named_upgradable_mutex::unlock_upgradable() +{ this->mutex()->unlock_upgradable(); } + +inline bool named_upgradable_mutex::try_lock_upgradable() +{ return this->mutex()->try_lock_upgradable(); } + +inline bool named_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; + } + return this->mutex()->timed_lock_upgradable(abs_time); +} + +inline void named_upgradable_mutex::lock_sharable() +{ this->mutex()->lock_sharable(); } + +inline void named_upgradable_mutex::unlock_sharable() +{ this->mutex()->unlock_sharable(); } + +inline bool named_upgradable_mutex::try_lock_sharable() +{ return this->mutex()->try_lock_sharable(); } + +inline bool named_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; + } + return this->mutex()->timed_lock_sharable(abs_time); +} + +inline void named_upgradable_mutex::unlock_and_lock_upgradable() +{ this->mutex()->unlock_and_lock_upgradable(); } + +inline void named_upgradable_mutex::unlock_and_lock_sharable() +{ this->mutex()->unlock_and_lock_sharable(); } + +inline void named_upgradable_mutex::unlock_upgradable_and_lock_sharable() +{ this->mutex()->unlock_upgradable_and_lock_sharable(); } + +inline void named_upgradable_mutex::unlock_upgradable_and_lock() +{ this->mutex()->unlock_upgradable_and_lock(); } + +inline bool named_upgradable_mutex::try_unlock_upgradable_and_lock() +{ return this->mutex()->try_unlock_upgradable_and_lock(); } + +inline bool named_upgradable_mutex::timed_unlock_upgradable_and_lock + (const boost::posix_time::ptime &abs_time) +{ return this->mutex()->timed_unlock_upgradable_and_lock(abs_time); } + +inline bool named_upgradable_mutex::try_unlock_sharable_and_lock() +{ return this->mutex()->try_unlock_sharable_and_lock(); } + +inline bool named_upgradable_mutex::try_unlock_sharable_and_lock_upgradable() +{ return this->mutex()->try_unlock_sharable_and_lock_upgradable(); } + +inline bool named_upgradable_mutex::remove(const char *name) +{ return shared_memory_object::remove(name); } + +/// @endcond + +} //namespace interprocess { +} //namespace boost { + +#include <boost/interprocess/detail/config_end.hpp> + +#endif //BOOST_INTERPROCESS_named_upgradable_mutex_HPP diff --git a/boost/interprocess/sync/null_mutex.hpp b/boost/interprocess/sync/null_mutex.hpp new file mode 100644 index 0000000000..afe444ed3f --- /dev/null +++ b/boost/interprocess/sync/null_mutex.hpp @@ -0,0 +1,147 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2005-2011. 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_NULL_MUTEX_HPP +#define BOOST_INTERPROCESS_NULL_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> + + +//!\file +//!Describes null_mutex classes + +namespace boost { + +namespace posix_time +{ class ptime; } + +namespace interprocess { + +//!Implements a mutex that simulates a mutex without doing any operation and +//!simulates a successful operation. +class null_mutex +{ + /// @cond + null_mutex(const null_mutex&); + null_mutex &operator= (const null_mutex&); + /// @endcond + public: + + //!Constructor. + //!Empty. + null_mutex(){} + + //!Destructor. + //!Empty. + ~null_mutex(){} + + //!Simulates a mutex lock() operation. Empty function. + void lock(){} + + //!Simulates a mutex try_lock() operation. + //!Equivalent to "return true;" + bool try_lock() + { return true; } + + //!Simulates a mutex timed_lock() operation. + //!Equivalent to "return true;" + bool timed_lock(const boost::posix_time::ptime &) + { return true; } + + //!Simulates a mutex unlock() operation. + //!Empty function. + void unlock(){} + + //!Simulates a mutex lock_sharable() operation. + //!Empty function. + void lock_sharable(){} + + //!Simulates a mutex try_lock_sharable() operation. + //!Equivalent to "return true;" + bool try_lock_sharable() + { return true; } + + //!Simulates a mutex timed_lock_sharable() operation. + //!Equivalent to "return true;" + bool timed_lock_sharable(const boost::posix_time::ptime &) + { return true; } + + //!Simulates a mutex unlock_sharable() operation. + //!Empty function. + void unlock_sharable(){} + + //!Simulates a mutex lock_upgradable() operation. + //!Empty function. + void lock_upgradable(){} + + //!Simulates a mutex try_lock_upgradable() operation. + //!Equivalent to "return true;" + bool try_lock_upgradable() + { return true; } + + //!Simulates a mutex timed_lock_upgradable() operation. + //!Equivalent to "return true;" + bool timed_lock_upgradable(const boost::posix_time::ptime &) + { return true; } + + //!Simulates a mutex unlock_upgradable() operation. + //!Empty function. + void unlock_upgradable(){} + + //!Simulates unlock_and_lock_upgradable(). + //!Empty function. + void unlock_and_lock_upgradable(){} + + //!Simulates unlock_and_lock_sharable(). + //!Empty function. + void unlock_and_lock_sharable(){} + + //!Simulates unlock_upgradable_and_lock_sharable(). + //!Empty function. + void unlock_upgradable_and_lock_sharable(){} + + //Promotions + + //!Simulates unlock_upgradable_and_lock(). + //!Empty function. + void unlock_upgradable_and_lock(){} + + //!Simulates try_unlock_upgradable_and_lock(). + //!Equivalent to "return true;" + bool try_unlock_upgradable_and_lock() + { return true; } + + //!Simulates timed_unlock_upgradable_and_lock(). + //!Equivalent to "return true;" + bool timed_unlock_upgradable_and_lock(const boost::posix_time::ptime &) + { return true; } + + //!Simulates try_unlock_sharable_and_lock(). + //!Equivalent to "return true;" + bool try_unlock_sharable_and_lock() + { return true; } + + //!Simulates try_unlock_sharable_and_lock_upgradable(). + //!Equivalent to "return true;" + bool try_unlock_sharable_and_lock_upgradable() + { return true; } +}; + +} //namespace interprocess { +} //namespace boost { + +#include <boost/interprocess/detail/config_end.hpp> + +#endif //BOOST_INTERPROCESS_NULL_MUTEX_HPP diff --git a/boost/interprocess/sync/posix/condition.hpp b/boost/interprocess/sync/posix/condition.hpp new file mode 100644 index 0000000000..213e4ce8b5 --- /dev/null +++ b/boost/interprocess/sync/posix/condition.hpp @@ -0,0 +1,191 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2005-2011. 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_POSIX_CONDITION_HPP +#define BOOST_INTERPROCESS_POSIX_CONDITION_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 <pthread.h> +#include <errno.h> +#include <boost/interprocess/sync/posix/pthread_helpers.hpp> +#include <boost/interprocess/sync/posix/ptime_to_timespec.hpp> +#include <boost/interprocess/detail/posix_time_types_wrk.hpp> +#include <boost/interprocess/sync/posix/mutex.hpp> +#include <boost/assert.hpp> + +namespace boost { +namespace interprocess { +namespace ipcdetail { + +class posix_condition +{ + //Non-copyable + posix_condition(const posix_condition &); + posix_condition &operator=(const posix_condition &); + + public: + //!Constructs a posix_condition. On error throws interprocess_exception. + posix_condition(); + + //!Destroys *this + //!liberating system resources. + ~posix_condition(); + + //!If there is a thread waiting on *this, change that + //!thread's state to ready. Otherwise there is no effect. + void notify_one(); + + //!Change the state of all threads waiting on *this to ready. + //!If there are no waiting threads, notify_all() has no effect. + void notify_all(); + + //!Releases the lock on the posix_mutex object associated with lock, blocks + //!the current thread of execution until readied by a call to + //!this->notify_one() or this->notify_all(), and then reacquires the lock. + template <typename L> + void wait(L& lock) + { + if (!lock) + throw lock_exception(); + this->do_wait(*lock.mutex()); + } + + //!The same as: + //!while (!pred()) wait(lock) + template <typename L, typename Pr> + void wait(L& lock, Pr pred) + { + if (!lock) + throw lock_exception(); + + while (!pred()) + this->do_wait(*lock.mutex()); + } + + //!Releases the lock on the posix_mutex object associated with lock, blocks + //!the current thread of execution until readied by a call to + //!this->notify_one() or this->notify_all(), or until time abs_time is reached, + //!and then reacquires the lock. + //!Returns: false if time abs_time is reached, otherwise true. + template <typename L> + bool timed_wait(L& lock, const boost::posix_time::ptime &abs_time) + { + if(abs_time == boost::posix_time::pos_infin){ + this->wait(lock); + return true; + } + if (!lock) + throw lock_exception(); + return this->do_timed_wait(abs_time, *lock.mutex()); + } + + //!The same as: while (!pred()) { + //! if (!timed_wait(lock, abs_time)) return pred(); + //! } return true; + template <typename L, typename Pr> + bool timed_wait(L& lock, const boost::posix_time::ptime &abs_time, Pr pred) + { + if(abs_time == boost::posix_time::pos_infin){ + this->wait(lock, pred); + return true; + } + if (!lock) + throw lock_exception(); + while (!pred()){ + if (!this->do_timed_wait(abs_time, *lock.mutex())) + return pred(); + } + + return true; + } + + + void do_wait(posix_mutex &mut); + + bool do_timed_wait(const boost::posix_time::ptime &abs_time, posix_mutex &mut); + + private: + pthread_cond_t m_condition; +}; + +inline posix_condition::posix_condition() +{ + int res; + pthread_condattr_t cond_attr; + res = pthread_condattr_init(&cond_attr); + if(res != 0){ + throw interprocess_exception("pthread_condattr_init failed"); + } + res = pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED); + if(res != 0){ + pthread_condattr_destroy(&cond_attr); + throw interprocess_exception(res); + } + res = pthread_cond_init(&m_condition, &cond_attr); + pthread_condattr_destroy(&cond_attr); + if(res != 0){ + throw interprocess_exception(res); + } +} + +inline posix_condition::~posix_condition() +{ + int res = 0; + res = pthread_cond_destroy(&m_condition); + BOOST_ASSERT(res == 0); +} + +inline void posix_condition::notify_one() +{ + int res = 0; + res = pthread_cond_signal(&m_condition); + BOOST_ASSERT(res == 0); +} + +inline void posix_condition::notify_all() +{ + int res = 0; + res = pthread_cond_broadcast(&m_condition); + BOOST_ASSERT(res == 0); +} + +inline void posix_condition::do_wait(posix_mutex &mut) +{ + pthread_mutex_t* pmutex = &mut.m_mut; + int res = 0; + res = pthread_cond_wait(&m_condition, pmutex); + BOOST_ASSERT(res == 0); +} + +inline bool posix_condition::do_timed_wait + (const boost::posix_time::ptime &abs_time, posix_mutex &mut) +{ + timespec ts = ptime_to_timespec(abs_time); + pthread_mutex_t* pmutex = &mut.m_mut; + int res = 0; + res = pthread_cond_timedwait(&m_condition, pmutex, &ts); + BOOST_ASSERT(res == 0 || res == ETIMEDOUT); + + return res != ETIMEDOUT; +} + +} //namespace ipcdetail +} //namespace interprocess +} //namespace boost + +#include <boost/interprocess/detail/config_end.hpp> + +#endif //#ifndef BOOST_INTERPROCESS_POSIX_CONDITION_HPP diff --git a/boost/interprocess/sync/posix/mutex.hpp b/boost/interprocess/sync/posix/mutex.hpp new file mode 100644 index 0000000000..22e2ec04b9 --- /dev/null +++ b/boost/interprocess/sync/posix/mutex.hpp @@ -0,0 +1,152 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2005-2011. 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. +// +////////////////////////////////////////////////////////////////////////////// +// +// Parts of the pthread code come from Boost Threads code: +// +////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2001-2003 +// William E. Kempf +// +// Permission to use, copy, modify, distribute and sell this software +// and its documentation for any purpose is hereby granted without fee, +// provided that the above copyright notice appear in all copies and +// that both that copyright notice and this permission notice appear +// in supporting documentation. William E. Kempf makes no representations +// about the suitability of this software for any purpose. +// It is provided "as is" without express or implied warranty. +////////////////////////////////////////////////////////////////////////////// + +#ifndef BOOST_INTERPROCESS_DETAIL_POSIX_MUTEX_HPP +#define BOOST_INTERPROCESS_DETAIL_POSIX_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 <pthread.h> +#include <errno.h> +#include <boost/interprocess/exceptions.hpp> +#include <boost/interprocess/sync/posix/ptime_to_timespec.hpp> +#include <boost/interprocess/detail/posix_time_types_wrk.hpp> +#include <boost/interprocess/exceptions.hpp> +#include <boost/interprocess/sync/posix/pthread_helpers.hpp> + +#ifndef BOOST_INTERPROCESS_POSIX_TIMEOUTS +# include <boost/interprocess/detail/os_thread_functions.hpp> +#endif +#include <boost/assert.hpp> + +namespace boost { +namespace interprocess { +namespace ipcdetail { + +class posix_condition; + +class posix_mutex +{ + posix_mutex(const posix_mutex &); + posix_mutex &operator=(const posix_mutex &); + public: + + posix_mutex(); + ~posix_mutex(); + + void lock(); + bool try_lock(); + bool timed_lock(const boost::posix_time::ptime &abs_time); + void unlock(); + + friend class posix_condition; + + private: + pthread_mutex_t m_mut; +}; + +inline posix_mutex::posix_mutex() +{ + mutexattr_wrapper mut_attr; + mutex_initializer mut(m_mut, mut_attr); + mut.release(); +} + +inline posix_mutex::~posix_mutex() +{ + int res = pthread_mutex_destroy(&m_mut); + BOOST_ASSERT(res == 0);(void)res; +} + +inline void posix_mutex::lock() +{ + if (pthread_mutex_lock(&m_mut) != 0) + throw lock_exception(); +} + +inline bool posix_mutex::try_lock() +{ + int res = pthread_mutex_trylock(&m_mut); + if (!(res == 0 || res == EBUSY)) + throw lock_exception(); + return res == 0; +} + +inline bool posix_mutex::timed_lock(const boost::posix_time::ptime &abs_time) +{ + if(abs_time == boost::posix_time::pos_infin){ + this->lock(); + return true; + } + #ifdef BOOST_INTERPROCESS_POSIX_TIMEOUTS + + timespec ts = ptime_to_timespec(abs_time); + int res = pthread_mutex_timedlock(&m_mut, &ts); + if (res != 0 && res != ETIMEDOUT) + throw lock_exception(); + return res == 0; + + #else //BOOST_INTERPROCESS_POSIX_TIMEOUTS + + //Obtain current count and target time + boost::posix_time::ptime now = microsec_clock::universal_time(); + + do{ + if(this->try_lock()){ + break; + } + now = microsec_clock::universal_time(); + + if(now >= abs_time){ + return false; + } + // relinquish current time slice + thread_yield(); + }while (true); + return true; + + #endif //BOOST_INTERPROCESS_POSIX_TIMEOUTS +} + +inline void posix_mutex::unlock() +{ + int res = 0; + res = pthread_mutex_unlock(&m_mut); + BOOST_ASSERT(res == 0); +} + +} //namespace ipcdetail { +} //namespace interprocess { +} //namespace boost { + +#include <boost/interprocess/detail/config_end.hpp> + +#endif //#ifndef BOOST_INTERPROCESS_DETAIL_POSIX_MUTEX_HPP diff --git a/boost/interprocess/sync/posix/named_mutex.hpp b/boost/interprocess/sync/posix/named_mutex.hpp new file mode 100644 index 0000000000..931c731eff --- /dev/null +++ b/boost/interprocess/sync/posix/named_mutex.hpp @@ -0,0 +1,116 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2005-2011. 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_POSIX_NAMED_MUTEX_HPP +#define BOOST_INTERPROCESS_POSIX_NAMED_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/creation_tags.hpp> +#include <boost/interprocess/exceptions.hpp> +#include <boost/interprocess/detail/interprocess_tester.hpp> +#include <boost/interprocess/permissions.hpp> + +#include <boost/interprocess/sync/posix/named_semaphore.hpp> + +namespace boost { +namespace interprocess { +namespace ipcdetail { + +class named_condition; + +class posix_named_mutex +{ + /// @cond + + posix_named_mutex(); + posix_named_mutex(const posix_named_mutex &); + posix_named_mutex &operator=(const posix_named_mutex &); + friend class named_condition; + /// @endcond + + public: + posix_named_mutex(create_only_t create_only, const char *name, const permissions &perm = permissions()); + + posix_named_mutex(open_or_create_t open_or_create, const char *name, const permissions &perm = permissions()); + + posix_named_mutex(open_only_t open_only, const char *name); + + ~posix_named_mutex(); + + void unlock(); + void lock(); + bool try_lock(); + bool timed_lock(const boost::posix_time::ptime &abs_time); + static bool remove(const char *name); + + /// @cond + private: + friend class interprocess_tester; + void dont_close_on_destruction(); + + posix_named_semaphore m_sem; + /// @endcond +}; + +/// @cond + +inline posix_named_mutex::posix_named_mutex(create_only_t, const char *name, const permissions &perm) + : m_sem(create_only, name, 1, perm) +{} + +inline posix_named_mutex::posix_named_mutex(open_or_create_t, const char *name, const permissions &perm) + : m_sem(open_or_create, name, 1, perm) +{} + +inline posix_named_mutex::posix_named_mutex(open_only_t, const char *name) + : m_sem(open_only, name) +{} + +inline void posix_named_mutex::dont_close_on_destruction() +{ interprocess_tester::dont_close_on_destruction(m_sem); } + +inline posix_named_mutex::~posix_named_mutex() +{} + +inline void posix_named_mutex::lock() +{ m_sem.wait(); } + +inline void posix_named_mutex::unlock() +{ m_sem.post(); } + +inline bool posix_named_mutex::try_lock() +{ return m_sem.try_wait(); } + +inline bool posix_named_mutex::timed_lock(const boost::posix_time::ptime &abs_time) +{ + if(abs_time == boost::posix_time::pos_infin){ + this->lock(); + return true; + } + return m_sem.timed_wait(abs_time); +} + +inline bool posix_named_mutex::remove(const char *name) +{ return posix_named_semaphore::remove(name); } + +/// @endcond + +} //namespace ipcdetail { +} //namespace interprocess { +} //namespace boost { + +#include <boost/interprocess/detail/config_end.hpp> + +#endif //BOOST_INTERPROCESS_POSIX_NAMED_MUTEX_HPP diff --git a/boost/interprocess/sync/posix/named_semaphore.hpp b/boost/interprocess/sync/posix/named_semaphore.hpp new file mode 100644 index 0000000000..f0327a4618 --- /dev/null +++ b/boost/interprocess/sync/posix/named_semaphore.hpp @@ -0,0 +1,84 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2005-2011. 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_POSIX_NAMED_CONDITION_HPP +#define BOOST_INTERPROCESS_POSIX_NAMED_CONDITION_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/posix/semaphore_wrapper.hpp> + +namespace boost { +namespace interprocess { + +/// @cond +namespace ipcdetail{ class interprocess_tester; } +/// @endcond + +namespace ipcdetail { + +class posix_named_semaphore +{ + posix_named_semaphore(); + posix_named_semaphore(const posix_named_semaphore&); + posix_named_semaphore &operator= (const posix_named_semaphore &); + + public: + posix_named_semaphore + (create_only_t, const char *name, unsigned int initialCount, const permissions &perm = permissions()) + { semaphore_open(mp_sem, DoCreate, name, initialCount, perm); } + + posix_named_semaphore(open_or_create_t, const char *name, unsigned int initialCount, const permissions &perm = permissions()) + { semaphore_open(mp_sem, DoOpenOrCreate, name, initialCount, perm); } + + posix_named_semaphore(open_only_t, const char *name) + { semaphore_open(mp_sem, DoOpen, name); } + + ~posix_named_semaphore() + { + if(mp_sem != BOOST_INTERPROCESS_POSIX_SEM_FAILED) + semaphore_close(mp_sem); + } + + void post() + { semaphore_post(mp_sem); } + + void wait() + { semaphore_wait(mp_sem); } + + bool try_wait() + { return semaphore_try_wait(mp_sem); } + + bool timed_wait(const boost::posix_time::ptime &abs_time) + { return semaphore_timed_wait(mp_sem, abs_time); } + + static bool remove(const char *name) + { return semaphore_unlink(name); } + + private: + friend class ipcdetail::interprocess_tester; + void dont_close_on_destruction() + { mp_sem = BOOST_INTERPROCESS_POSIX_SEM_FAILED; } + + sem_t *mp_sem; +}; + +} //namespace ipcdetail { +} //namespace interprocess { +} //namespace boost { + +#include <boost/interprocess/detail/config_end.hpp> + +#endif //#ifndef BOOST_INTERPROCESS_POSIX_NAMED_CONDITION_HPP diff --git a/boost/interprocess/sync/posix/pthread_helpers.hpp b/boost/interprocess/sync/posix/pthread_helpers.hpp new file mode 100644 index 0000000000..c09ce200cf --- /dev/null +++ b/boost/interprocess/sync/posix/pthread_helpers.hpp @@ -0,0 +1,168 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2005-2011. 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_PTHREAD_HELPERS_HPP +#define BOOST_INTERPROCESS_PTHREAD_HELPERS_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 <pthread.h> +#include <errno.h> +#include <boost/interprocess/exceptions.hpp> + +namespace boost { +namespace interprocess { +namespace ipcdetail{ + + #if defined BOOST_INTERPROCESS_POSIX_PROCESS_SHARED + + //!Makes pthread_mutexattr_t cleanup easy when using exceptions + struct mutexattr_wrapper + { + //!Constructor + mutexattr_wrapper(bool recursive = false) + { + if(pthread_mutexattr_init(&m_attr)!=0 || + pthread_mutexattr_setpshared(&m_attr, PTHREAD_PROCESS_SHARED)!= 0 || + (recursive && + pthread_mutexattr_settype(&m_attr, PTHREAD_MUTEX_RECURSIVE)!= 0 )) + throw interprocess_exception("pthread_mutexattr_xxxx failed"); + } + + //!Destructor + ~mutexattr_wrapper() { pthread_mutexattr_destroy(&m_attr); } + + //!This allows using mutexattr_wrapper as pthread_mutexattr_t + operator pthread_mutexattr_t&() { return m_attr; } + + pthread_mutexattr_t m_attr; + }; + + //!Makes pthread_condattr_t cleanup easy when using exceptions + struct condattr_wrapper + { + //!Constructor + condattr_wrapper() + { + if(pthread_condattr_init(&m_attr)!=0 || + pthread_condattr_setpshared(&m_attr, PTHREAD_PROCESS_SHARED)!= 0) + throw interprocess_exception("pthread_condattr_xxxx failed"); + } + + //!Destructor + ~condattr_wrapper() { pthread_condattr_destroy(&m_attr); } + + //!This allows using condattr_wrapper as pthread_condattr_t + operator pthread_condattr_t&(){ return m_attr; } + + pthread_condattr_t m_attr; + }; + + //!Makes initialized pthread_mutex_t cleanup easy when using exceptions + class mutex_initializer + { + public: + //!Constructor. Takes interprocess_mutex attributes to initialize the interprocess_mutex + mutex_initializer(pthread_mutex_t &mut, pthread_mutexattr_t &mut_attr) + : mp_mut(&mut) + { + if(pthread_mutex_init(mp_mut, &mut_attr) != 0) + throw interprocess_exception("pthread_mutex_init failed"); + } + + ~mutex_initializer() { if(mp_mut) pthread_mutex_destroy(mp_mut); } + + void release() {mp_mut = 0; } + + private: + pthread_mutex_t *mp_mut; + }; + + //!Makes initialized pthread_cond_t cleanup easy when using exceptions + class condition_initializer + { + public: + condition_initializer(pthread_cond_t &cond, pthread_condattr_t &cond_attr) + : mp_cond(&cond) + { + if(pthread_cond_init(mp_cond, &cond_attr)!= 0) + throw interprocess_exception("pthread_cond_init failed"); + } + + ~condition_initializer() { if(mp_cond) pthread_cond_destroy(mp_cond); } + + void release() { mp_cond = 0; } + + private: + pthread_cond_t *mp_cond; + }; + + #endif // #if defined BOOST_INTERPROCESS_POSIX_PROCESS_SHARED + + #if defined(BOOST_INTERPROCESS_POSIX_BARRIERS) && defined(BOOST_INTERPROCESS_POSIX_PROCESS_SHARED) + + //!Makes pthread_barrierattr_t cleanup easy when using exceptions + struct barrierattr_wrapper + { + //!Constructor + barrierattr_wrapper() + { + if(pthread_barrierattr_init(&m_attr)!=0 || + pthread_barrierattr_setpshared(&m_attr, PTHREAD_PROCESS_SHARED)!= 0) + throw interprocess_exception("pthread_barrierattr_xxx failed"); + } + + //!Destructor + ~barrierattr_wrapper() { pthread_barrierattr_destroy(&m_attr); } + + //!This allows using mutexattr_wrapper as pthread_barrierattr_t + operator pthread_barrierattr_t&() { return m_attr; } + + pthread_barrierattr_t m_attr; + }; + + //!Makes initialized pthread_barrier_t cleanup easy when using exceptions + class barrier_initializer + { + public: + //!Constructor. Takes barrier attributes to initialize the barrier + barrier_initializer(pthread_barrier_t &mut, + pthread_barrierattr_t &mut_attr, + int count) + : mp_barrier(&mut) + { + if(pthread_barrier_init(mp_barrier, &mut_attr, count) != 0) + throw interprocess_exception("pthread_barrier_init failed"); + } + + ~barrier_initializer() { if(mp_barrier) pthread_barrier_destroy(mp_barrier); } + + void release() {mp_barrier = 0; } + + private: + pthread_barrier_t *mp_barrier; + }; + + #endif //#if defined(BOOST_INTERPROCESS_POSIX_BARRIERS) && defined(BOOST_INTERPROCESS_POSIX_PROCESS_SHARED) + +}//namespace ipcdetail + +}//namespace interprocess + +}//namespace boost + +#include <boost/interprocess/detail/config_end.hpp> + +#endif //ifdef BOOST_INTERPROCESS_PTHREAD_HELPERS_HPP diff --git a/boost/interprocess/sync/posix/ptime_to_timespec.hpp b/boost/interprocess/sync/posix/ptime_to_timespec.hpp new file mode 100644 index 0000000000..7d787353bd --- /dev/null +++ b/boost/interprocess/sync/posix/ptime_to_timespec.hpp @@ -0,0 +1,38 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2005-2011. 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_DETAIL_PTIME_TO_TIMESPEC_HPP +#define BOOST_INTERPROCESS_DETAIL_PTIME_TO_TIMESPEC_HPP + +#include <boost/interprocess/detail/posix_time_types_wrk.hpp> + +namespace boost { + +namespace interprocess { + +namespace ipcdetail { + +inline timespec ptime_to_timespec (const boost::posix_time::ptime &tm) +{ + const boost::posix_time::ptime epoch(boost::gregorian::date(1970,1,1)); + boost::posix_time::time_duration duration (tm - epoch); + timespec ts; + ts.tv_sec = duration.total_seconds(); + ts.tv_nsec = duration.total_nanoseconds() % 1000000000; + return ts; +} + +} //namespace ipcdetail { + +} //namespace interprocess { + +} //namespace boost { + +#endif //ifndef BOOST_INTERPROCESS_DETAIL_PTIME_TO_TIMESPEC_HPP diff --git a/boost/interprocess/sync/posix/recursive_mutex.hpp b/boost/interprocess/sync/posix/recursive_mutex.hpp new file mode 100644 index 0000000000..baa670fa9a --- /dev/null +++ b/boost/interprocess/sync/posix/recursive_mutex.hpp @@ -0,0 +1,142 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2005-2011. 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. +// +////////////////////////////////////////////////////////////////////////////// +// +// Parts of the pthread code come from Boost Threads code: +// +////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2001-2003 +// William E. Kempf +// +// Permission to use, copy, modify, distribute and sell this software +// and its documentation for any purpose is hereby granted without fee, +// provided that the above copyright notice appear in all copies and +// that both that copyright notice and this permission notice appear +// in supporting documentation. William E. Kempf makes no representations +// about the suitability of this software for any purpose. +// It is provided "as is" without express or implied warranty. +////////////////////////////////////////////////////////////////////////////// + +#ifndef BOOST_INTERPROCESS_DETAIL_POSIX_RECURSIVE_MUTEX_HPP +#define BOOST_INTERPROCESS_DETAIL_POSIX_RECURSIVE_MUTEX_HPP + +#include <boost/interprocess/detail/config_begin.hpp> +#include <boost/interprocess/detail/workaround.hpp> + +#include <pthread.h> +#include <errno.h> +#include <boost/interprocess/sync/posix/pthread_helpers.hpp> +#include <boost/interprocess/sync/posix/ptime_to_timespec.hpp> +#include <boost/interprocess/detail/posix_time_types_wrk.hpp> +#include <boost/interprocess/exceptions.hpp> +#ifndef BOOST_INTERPROCESS_POSIX_TIMEOUTS +# include <boost/interprocess/detail/os_thread_functions.hpp> +#endif +#include <boost/assert.hpp> + +namespace boost { +namespace interprocess { +namespace ipcdetail { + +class posix_recursive_mutex +{ + posix_recursive_mutex(const posix_recursive_mutex &); + posix_recursive_mutex &operator=(const posix_recursive_mutex &); + public: + + posix_recursive_mutex(); + ~posix_recursive_mutex(); + + void lock(); + bool try_lock(); + bool timed_lock(const boost::posix_time::ptime &abs_time); + void unlock(); + + private: + pthread_mutex_t m_mut; +}; + +inline posix_recursive_mutex::posix_recursive_mutex() +{ + mutexattr_wrapper mut_attr(true); + mutex_initializer mut(m_mut, mut_attr); + mut.release(); +} + +inline posix_recursive_mutex::~posix_recursive_mutex() +{ + int res = pthread_mutex_destroy(&m_mut); + BOOST_ASSERT(res == 0);(void)res; +} + +inline void posix_recursive_mutex::lock() +{ + if (pthread_mutex_lock(&m_mut) != 0) + throw lock_exception(); +} + +inline bool posix_recursive_mutex::try_lock() +{ + int res = pthread_mutex_trylock(&m_mut); + if (!(res == 0 || res == EBUSY)) + throw lock_exception(); + return res == 0; +} + +inline bool posix_recursive_mutex::timed_lock(const boost::posix_time::ptime &abs_time) +{ + if(abs_time == boost::posix_time::pos_infin){ + this->lock(); + return true; + } + #ifdef BOOST_INTERPROCESS_POSIX_TIMEOUTS + + timespec ts = ptime_to_timespec(abs_time); + int res = pthread_mutex_timedlock(&m_mut, &ts); + if (res != 0 && res != ETIMEDOUT) + throw lock_exception(); + return res == 0; + + #else //BOOST_INTERPROCESS_POSIX_TIMEOUTS + + //Obtain current count and target time + boost::posix_time::ptime now = microsec_clock::universal_time(); + + do{ + if(this->try_lock()){ + break; + } + now = microsec_clock::universal_time(); + + if(now >= abs_time){ + return false; + } + // relinquish current time slice + thread_yield(); + }while (true); + return true; + + #endif //BOOST_INTERPROCESS_POSIX_TIMEOUTS +} + +inline void posix_recursive_mutex::unlock() +{ + int res = 0; + res = pthread_mutex_unlock(&m_mut); + BOOST_ASSERT(res == 0); +} + +} //namespace ipcdetail { +} //namespace interprocess { +} //namespace boost { + +#include <boost/interprocess/detail/config_end.hpp> + +#endif //#ifndef BOOST_INTERPROCESS_DETAIL_POSIX_RECURSIVE_MUTEX_HPP diff --git a/boost/interprocess/sync/posix/semaphore.hpp b/boost/interprocess/sync/posix/semaphore.hpp new file mode 100644 index 0000000000..b7f62cc42d --- /dev/null +++ b/boost/interprocess/sync/posix/semaphore.hpp @@ -0,0 +1,63 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2005-2011. 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_POSIX_SEMAPHORE_HPP +#define BOOST_INTERPROCESS_POSIX_SEMAPHORE_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/detail/posix_time_types_wrk.hpp> +#include <boost/interprocess/sync/posix/semaphore_wrapper.hpp> + +namespace boost { +namespace interprocess { +namespace ipcdetail { + +class posix_semaphore +{ + posix_semaphore(); + posix_semaphore(const posix_semaphore&); + posix_semaphore &operator= (const posix_semaphore &); + + public: + posix_semaphore(unsigned int initialCount) + { semaphore_init(&m_sem, initialCount); } + + ~posix_semaphore() + { semaphore_destroy(&m_sem); } + + void post() + { semaphore_post(&m_sem); } + + void wait() + { semaphore_wait(&m_sem); } + + bool try_wait() + { return semaphore_try_wait(&m_sem); } + + bool timed_wait(const boost::posix_time::ptime &abs_time) + { return semaphore_timed_wait(&m_sem, abs_time); } + + private: + sem_t m_sem; +}; + +} //namespace ipcdetail { +} //namespace interprocess { +} //namespace boost { + +#include <boost/interprocess/detail/config_end.hpp> + +#endif //#ifndef BOOST_INTERPROCESS_POSIX_SEMAPHORE_HPP diff --git a/boost/interprocess/sync/posix/semaphore_wrapper.hpp b/boost/interprocess/sync/posix/semaphore_wrapper.hpp new file mode 100644 index 0000000000..1aeef47662 --- /dev/null +++ b/boost/interprocess/sync/posix/semaphore_wrapper.hpp @@ -0,0 +1,211 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2005-2011. 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_POSIX_SEMAPHORE_WRAPPER_HPP +#define BOOST_INTERPROCESS_POSIX_SEMAPHORE_WRAPPER_HPP + +#include <boost/interprocess/detail/posix_time_types_wrk.hpp> +#include <boost/interprocess/exceptions.hpp> +#include <boost/interprocess/creation_tags.hpp> +#include <boost/interprocess/detail/os_file_functions.hpp> +#include <boost/interprocess/detail/tmp_dir_helpers.hpp> +#include <boost/interprocess/permissions.hpp> + +#include <fcntl.h> //O_CREAT, O_*... +#include <unistd.h> //close +#include <string> //std::string +#include <semaphore.h> //sem_* family, SEM_VALUE_MAX +#include <sys/stat.h> //mode_t, S_IRWXG, S_IRWXO, S_IRWXU, +#include <boost/assert.hpp> + +#ifdef SEM_FAILED +#define BOOST_INTERPROCESS_POSIX_SEM_FAILED (reinterpret_cast<sem_t*>(SEM_FAILED)) +#else +#define BOOST_INTERPROCESS_POSIX_SEM_FAILED (reinterpret_cast<sem_t*>(-1)) +#endif + +#ifdef BOOST_INTERPROCESS_POSIX_TIMEOUTS +#include <boost/interprocess/sync/posix/ptime_to_timespec.hpp> +#else +#include <boost/interprocess/detail/os_thread_functions.hpp> +#endif + +namespace boost { +namespace interprocess { +namespace ipcdetail { + +inline bool semaphore_open + (sem_t *&handle, create_enum_t type, const char *origname, + unsigned int count = 0, const permissions &perm = permissions()) +{ + std::string name; + #ifndef BOOST_INTERPROCESS_FILESYSTEM_BASED_POSIX_SEMAPHORES + add_leading_slash(origname, name); + #else + create_tmp_and_clean_old_and_get_filename(origname, name); + #endif + + //Create new mapping + int oflag = 0; + switch(type){ + case DoOpen: + { + //No addition + handle = ::sem_open(name.c_str(), oflag); + } + break; + case DoOpenOrCreate: + case DoCreate: + { + while(1){ + oflag = (O_CREAT | O_EXCL); + handle = ::sem_open(name.c_str(), oflag, perm.get_permissions(), count); + if(handle != BOOST_INTERPROCESS_POSIX_SEM_FAILED){ + //We can't change semaphore permissions! + //::fchmod(handle, perm.get_permissions()); + break; + } + else if(errno == EEXIST && type == DoOpenOrCreate){ + oflag = 0; + if( (handle = ::sem_open(name.c_str(), oflag)) != BOOST_INTERPROCESS_POSIX_SEM_FAILED + || (errno != ENOENT) ){ + break; + } + } + else{ + break; + } + } + } + break; + default: + { + error_info err(other_error); + throw interprocess_exception(err); + } + } + + //Check for error + if(handle == BOOST_INTERPROCESS_POSIX_SEM_FAILED){ + throw interprocess_exception(error_info(errno)); + } + + return true; +} + +inline void semaphore_close(sem_t *handle) +{ + int ret = sem_close(handle); + if(ret != 0){ + BOOST_ASSERT(0); + } +} + +inline bool semaphore_unlink(const char *semname) +{ + try{ + std::string sem_str; + #ifndef BOOST_INTERPROCESS_FILESYSTEM_BASED_POSIX_SEMAPHORES + add_leading_slash(semname, sem_str); + #else + tmp_filename(semname, sem_str); + #endif + return 0 == sem_unlink(sem_str.c_str()); + } + catch(...){ + return false; + } +} + +inline void semaphore_init(sem_t *handle, unsigned int initialCount) +{ + int ret = sem_init(handle, 1, initialCount); + //According to SUSV3 version 2003 edition, the return value of a successful + //sem_init call is not defined, but -1 is returned on failure. + //In the future, a successful call might be required to return 0. + if(ret == -1){ + throw interprocess_exception(system_error_code()); + } +} + +inline void semaphore_destroy(sem_t *handle) +{ + int ret = sem_destroy(handle); + if(ret != 0){ + BOOST_ASSERT(0); + } +} + +inline void semaphore_post(sem_t *handle) +{ + int ret = sem_post(handle); + if(ret != 0){ + throw interprocess_exception(system_error_code()); + } +} + +inline void semaphore_wait(sem_t *handle) +{ + int ret = sem_wait(handle); + if(ret != 0){ + throw interprocess_exception(system_error_code()); + } +} + +inline bool semaphore_try_wait(sem_t *handle) +{ + int res = sem_trywait(handle); + if(res == 0) + return true; + if(system_error_code() == EAGAIN){ + return false; + } + throw interprocess_exception(system_error_code()); + return false; +} + +inline bool semaphore_timed_wait(sem_t *handle, const boost::posix_time::ptime &abs_time) +{ + if(abs_time == boost::posix_time::pos_infin){ + semaphore_wait(handle); + return true; + } + #ifdef BOOST_INTERPROCESS_POSIX_TIMEOUTS + timespec tspec = ptime_to_timespec(abs_time); + for (;;){ + int res = sem_timedwait(handle, &tspec); + if(res == 0) + return true; + if (res > 0){ + //buggy glibc, copy the returned error code to errno + errno = res; + } + if(system_error_code() == ETIMEDOUT){ + return false; + } + throw interprocess_exception(system_error_code()); + } + return false; + #else //#ifdef BOOST_INTERPROCESS_POSIX_TIMEOUTS + boost::posix_time::ptime now; + do{ + if(semaphore_try_wait(handle)) + return true; + thread_yield(); + }while((now = microsec_clock::universal_time()) < abs_time); + return false; + #endif //#ifdef BOOST_INTERPROCESS_POSIX_TIMEOUTS +} + +} //namespace ipcdetail { +} //namespace interprocess { +} //namespace boost { + +#endif //#ifndef BOOST_INTERPROCESS_POSIX_SEMAPHORE_WRAPPER_HPP diff --git a/boost/interprocess/sync/scoped_lock.hpp b/boost/interprocess/sync/scoped_lock.hpp new file mode 100644 index 0000000000..bfef63a30a --- /dev/null +++ b/boost/interprocess/sync/scoped_lock.hpp @@ -0,0 +1,372 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2005-2011. 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. +// +////////////////////////////////////////////////////////////////////////////// +// +// This interface is inspired by Howard Hinnant's lock proposal. +// http://home.twcny.rr.com/hinnant/cpp_extensions/threads_move.html +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef BOOST_INTERPROCESS_SCOPED_LOCK_HPP +#define BOOST_INTERPROCESS_SCOPED_LOCK_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/interprocess_fwd.hpp> +#include <boost/interprocess/sync/lock_options.hpp> +#include <boost/interprocess/exceptions.hpp> +#include <boost/interprocess/detail/mpl.hpp> +#include <boost/interprocess/detail/type_traits.hpp> +#include <boost/move/move.hpp> +#include <boost/interprocess/detail/posix_time_types_wrk.hpp> + +//!\file +//!Describes the scoped_lock class. + +namespace boost { +namespace interprocess { + + +//!scoped_lock is meant to carry out the tasks for locking, unlocking, try-locking +//!and timed-locking (recursive or not) for the Mutex. The Mutex need not supply all +//!of this functionality. If the client of scoped_lock<Mutex> does not use +//!functionality which the Mutex does not supply, no harm is done. Mutex ownership +//!transfer is supported through the syntax of move semantics. Ownership transfer +//!is allowed both by construction and assignment. The scoped_lock does not support +//!copy semantics. A compile time error results if copy construction or copy +//!assignment is attempted. Mutex ownership can also be moved from an +//!upgradable_lock and sharable_lock via constructor. In this role, scoped_lock +//!shares the same functionality as a write_lock. +template <class Mutex> +class scoped_lock +{ + /// @cond + private: + typedef scoped_lock<Mutex> this_type; + BOOST_MOVABLE_BUT_NOT_COPYABLE(scoped_lock) + typedef bool this_type::*unspecified_bool_type; + /// @endcond + public: + + typedef Mutex mutex_type; + + //!Effects: Default constructs a scoped_lock. + //!Postconditions: owns() == false and mutex() == 0. + scoped_lock() + : mp_mutex(0), m_locked(false) + {} + + //!Effects: m.lock(). + //!Postconditions: owns() == true and mutex() == &m. + //!Notes: The constructor will take ownership of the mutex. If another thread + //! already owns the mutex, this thread will block until the mutex is released. + //! Whether or not this constructor handles recursive locking depends upon the mutex. + explicit scoped_lock(mutex_type& m) + : mp_mutex(&m), m_locked(false) + { mp_mutex->lock(); m_locked = true; } + + //!Postconditions: owns() == false, and mutex() == &m. + //!Notes: The constructor will not take ownership of the mutex. There is no effect + //! required on the referenced mutex. + scoped_lock(mutex_type& m, defer_lock_type) + : mp_mutex(&m), m_locked(false) + {} + + //!Postconditions: owns() == true, and mutex() == &m. + //!Notes: The constructor will suppose that the mutex is already locked. There + //! is no effect required on the referenced mutex. + scoped_lock(mutex_type& m, accept_ownership_type) + : mp_mutex(&m), m_locked(true) + {} + + //!Effects: m.try_lock(). + //!Postconditions: mutex() == &m. owns() == the return value of the + //! m.try_lock() executed within the constructor. + //!Notes: The constructor will take ownership of the mutex if it can do + //! so without waiting. Whether or not this constructor handles recursive + //! locking depends upon the mutex. If the mutex_type does not support try_lock, + //! this constructor will fail at compile time if instantiated, but otherwise + //! have no effect. + scoped_lock(mutex_type& m, try_to_lock_type) + : mp_mutex(&m), m_locked(mp_mutex->try_lock()) + {} + + //!Effects: m.timed_lock(abs_time). + //!Postconditions: mutex() == &m. owns() == the return value of the + //! m.timed_lock(abs_time) executed within the constructor. + //!Notes: The constructor will take ownership of the mutex if it can do + //! it until abs_time is reached. Whether or not this constructor + //! handles recursive locking depends upon the mutex. If the mutex_type + //! does not support try_lock, this constructor will fail at compile + //! time if instantiated, but otherwise have no effect. + scoped_lock(mutex_type& m, const boost::posix_time::ptime& abs_time) + : mp_mutex(&m), m_locked(mp_mutex->timed_lock(abs_time)) + {} + + //!Postconditions: mutex() == the value scop.mutex() had before the + //! constructor executes. s1.mutex() == 0. owns() == the value of + //! scop.owns() before the constructor executes. scop.owns(). + //!Notes: If the scop scoped_lock owns the mutex, ownership is moved + //! to thisscoped_lock with no blocking. If the scop scoped_lock does not + //! own the mutex, then neither will this scoped_lock. Only a moved + //! scoped_lock's will match this signature. An non-moved scoped_lock + //! can be moved with the expression: "boost::move(lock);". This + //! constructor does not alter the state of the mutex, only potentially + //! who owns it. + scoped_lock(BOOST_RV_REF(scoped_lock) scop) + : mp_mutex(0), m_locked(scop.owns()) + { mp_mutex = scop.release(); } + + //!Effects: If upgr.owns() then calls unlock_upgradable_and_lock() on the + //! referenced mutex. upgr.release() is called. + //!Postconditions: mutex() == the value upgr.mutex() had before the construction. + //! upgr.mutex() == 0. owns() == upgr.owns() before the construction. + //! upgr.owns() == false after the construction. + //!Notes: If upgr is locked, this constructor will lock this scoped_lock while + //! unlocking upgr. If upgr is unlocked, then this scoped_lock will be + //! unlocked as well. Only a moved upgradable_lock's will match this + //! signature. An non-moved upgradable_lock can be moved with + //! the expression: "boost::move(lock);" This constructor may block if + //! other threads hold a sharable_lock on this mutex (sharable_lock's can + //! share ownership with an upgradable_lock). + template<class T> + explicit scoped_lock(BOOST_RV_REF(upgradable_lock<T>) upgr + , typename ipcdetail::enable_if< ipcdetail::is_same<T, Mutex> >::type * = 0) + : mp_mutex(0), m_locked(false) + { + upgradable_lock<mutex_type> &u_lock = upgr; + if(u_lock.owns()){ + u_lock.mutex()->unlock_upgradable_and_lock(); + m_locked = true; + } + mp_mutex = u_lock.release(); + } + + //!Effects: If upgr.owns() then calls try_unlock_upgradable_and_lock() on the + //!referenced mutex: + //! a)if try_unlock_upgradable_and_lock() returns true then mutex() obtains + //! the value from upgr.release() and owns() is set to true. + //! b)if try_unlock_upgradable_and_lock() returns false then upgr is + //! unaffected and this scoped_lock construction as the same effects as + //! a default construction. + //! c)Else upgr.owns() is false. mutex() obtains the value from upgr.release() + //! and owns() is set to false + //!Notes: This construction will not block. It will try to obtain mutex + //! ownership from upgr immediately, while changing the lock type from a + //! "read lock" to a "write lock". If the "read lock" isn't held in the + //! first place, the mutex merely changes type to an unlocked "write lock". + //! If the "read lock" is held, then mutex transfer occurs only if it can + //! do so in a non-blocking manner. + template<class T> + scoped_lock(BOOST_RV_REF(upgradable_lock<T>) upgr, try_to_lock_type + , typename ipcdetail::enable_if< ipcdetail::is_same<T, Mutex> >::type * = 0) + : mp_mutex(0), m_locked(false) + { + upgradable_lock<mutex_type> &u_lock = upgr; + if(u_lock.owns()){ + if((m_locked = u_lock.mutex()->try_unlock_upgradable_and_lock()) == true){ + mp_mutex = u_lock.release(); + } + } + else{ + u_lock.release(); + } + } + + //!Effects: If upgr.owns() then calls timed_unlock_upgradable_and_lock(abs_time) + //! on the referenced mutex: + //! a)if timed_unlock_upgradable_and_lock(abs_time) returns true then mutex() + //! obtains the value from upgr.release() and owns() is set to true. + //! b)if timed_unlock_upgradable_and_lock(abs_time) returns false then upgr + //! is unaffected and this scoped_lock construction as the same effects + //! as a default construction. + //! c)Else upgr.owns() is false. mutex() obtains the value from upgr.release() + //! and owns() is set to false + //!Notes: This construction will not block. It will try to obtain mutex ownership + //! from upgr immediately, while changing the lock type from a "read lock" to a + //! "write lock". If the "read lock" isn't held in the first place, the mutex + //! merely changes type to an unlocked "write lock". If the "read lock" is held, + //! then mutex transfer occurs only if it can do so in a non-blocking manner. + template<class T> + scoped_lock(BOOST_RV_REF(upgradable_lock<T>) upgr, boost::posix_time::ptime &abs_time + , typename ipcdetail::enable_if< ipcdetail::is_same<T, Mutex> >::type * = 0) + : mp_mutex(0), m_locked(false) + { + upgradable_lock<mutex_type> &u_lock = upgr; + if(u_lock.owns()){ + if((m_locked = u_lock.mutex()->timed_unlock_upgradable_and_lock(abs_time)) == true){ + mp_mutex = u_lock.release(); + } + } + else{ + u_lock.release(); + } + } + + //!Effects: If shar.owns() then calls try_unlock_sharable_and_lock() on the + //!referenced mutex. + //! a)if try_unlock_sharable_and_lock() returns true then mutex() obtains + //! the value from shar.release() and owns() is set to true. + //! b)if try_unlock_sharable_and_lock() returns false then shar is + //! unaffected and this scoped_lock construction has the same + //! effects as a default construction. + //! c)Else shar.owns() is false. mutex() obtains the value from + //! shar.release() and owns() is set to false + //!Notes: This construction will not block. It will try to obtain mutex + //! ownership from shar immediately, while changing the lock type from a + //! "read lock" to a "write lock". If the "read lock" isn't held in the + //! first place, the mutex merely changes type to an unlocked "write lock". + //! If the "read lock" is held, then mutex transfer occurs only if it can + //! do so in a non-blocking manner. + template<class T> + scoped_lock(BOOST_RV_REF(sharable_lock<T>) shar, try_to_lock_type + , typename ipcdetail::enable_if< ipcdetail::is_same<T, Mutex> >::type * = 0) + : mp_mutex(0), m_locked(false) + { + sharable_lock<mutex_type> &s_lock = shar; + if(s_lock.owns()){ + if((m_locked = s_lock.mutex()->try_unlock_sharable_and_lock()) == true){ + mp_mutex = s_lock.release(); + } + } + else{ + s_lock.release(); + } + } + + //!Effects: if (owns()) mp_mutex->unlock(). + //!Notes: The destructor behavior ensures that the mutex lock is not leaked.*/ + ~scoped_lock() + { + try{ if(m_locked && mp_mutex) mp_mutex->unlock(); } + catch(...){} + } + + //!Effects: If owns() before the call, then unlock() is called on mutex(). + //! *this gets the state of scop and scop gets set to a default constructed state. + //!Notes: With a recursive mutex it is possible that both this and scop own + //! the same mutex before the assignment. In this case, this will own the + //! mutex after the assignment (and scop will not), but the mutex's lock + //! count will be decremented by one. + scoped_lock &operator=(BOOST_RV_REF(scoped_lock) scop) + { + if(this->owns()) + this->unlock(); + m_locked = scop.owns(); + mp_mutex = scop.release(); + return *this; + } + + //!Effects: If mutex() == 0 or if already locked, throws a lock_exception() + //! exception. Calls lock() on the referenced mutex. + //!Postconditions: owns() == true. + //!Notes: The scoped_lock changes from a state of not owning the mutex, to + //! owning the mutex, blocking if necessary. + void lock() + { + if(!mp_mutex || m_locked) + throw lock_exception(); + mp_mutex->lock(); + m_locked = true; + } + + //!Effects: If mutex() == 0 or if already locked, throws a lock_exception() + //! exception. Calls try_lock() on the referenced mutex. + //!Postconditions: owns() == the value returned from mutex()->try_lock(). + //!Notes: The scoped_lock changes from a state of not owning the mutex, to + //! owning the mutex, but only if blocking was not required. If the + //! mutex_type does not support try_lock(), this function will fail at + //! compile time if instantiated, but otherwise have no effect.*/ + bool try_lock() + { + if(!mp_mutex || m_locked) + throw lock_exception(); + m_locked = mp_mutex->try_lock(); + return m_locked; + } + + //!Effects: If mutex() == 0 or if already locked, throws a lock_exception() + //! exception. Calls timed_lock(abs_time) on the referenced mutex. + //!Postconditions: owns() == the value returned from mutex()-> timed_lock(abs_time). + //!Notes: The scoped_lock changes from a state of not owning the mutex, to + //! owning the mutex, but only if it can obtain ownership by the specified + //! time. If the mutex_type does not support timed_lock (), this function + //! will fail at compile time if instantiated, but otherwise have no effect.*/ + bool timed_lock(const boost::posix_time::ptime& abs_time) + { + if(!mp_mutex || m_locked) + throw lock_exception(); + m_locked = mp_mutex->timed_lock(abs_time); + return m_locked; + } + + //!Effects: If mutex() == 0 or if not locked, throws a lock_exception() + //! exception. Calls unlock() on the referenced mutex. + //!Postconditions: owns() == false. + //!Notes: The scoped_lock changes from a state of owning the mutex, to not + //! owning the mutex.*/ + void unlock() + { + if(!mp_mutex || !m_locked) + throw lock_exception(); + mp_mutex->unlock(); + m_locked = false; + } + + //!Effects: Returns true if this scoped_lock has acquired + //!the referenced mutex. + bool owns() const + { return m_locked && mp_mutex; } + + //!Conversion to bool. + //!Returns owns(). + operator unspecified_bool_type() const + { return m_locked? &this_type::m_locked : 0; } + + //!Effects: Returns a pointer to the referenced mutex, or 0 if + //!there is no mutex to reference. + mutex_type* mutex() const + { return mp_mutex; } + + //!Effects: Returns a pointer to the referenced mutex, or 0 if there is no + //! mutex to reference. + //!Postconditions: mutex() == 0 and owns() == false. + mutex_type* release() + { + mutex_type *mut = mp_mutex; + mp_mutex = 0; + m_locked = false; + return mut; + } + + //!Effects: Swaps state with moved lock. + //!Throws: Nothing. + void swap( scoped_lock<mutex_type> &other) + { + std::swap(mp_mutex, other.mp_mutex); + std::swap(m_locked, other.m_locked); + } + + /// @cond + private: + mutex_type *mp_mutex; + bool m_locked; + /// @endcond +}; + +} // namespace interprocess +} // namespace boost + +#include <boost/interprocess/detail/config_end.hpp> + +#endif // BOOST_INTERPROCESS_SCOPED_LOCK_HPP diff --git a/boost/interprocess/sync/sharable_lock.hpp b/boost/interprocess/sync/sharable_lock.hpp new file mode 100644 index 0000000000..c8b7c1d26e --- /dev/null +++ b/boost/interprocess/sync/sharable_lock.hpp @@ -0,0 +1,305 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2005-2011. 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. +// +////////////////////////////////////////////////////////////////////////////// +// +// This interface is inspired by Howard Hinnant's lock proposal. +// http://home.twcny.rr.com/hinnant/cpp_extensions/threads_move.html +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef BOOST_INTERPROCESS_SHARABLE_LOCK_HPP +#define BOOST_INTERPROCESS_SHARABLE_LOCK_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/interprocess_fwd.hpp> +#include <boost/interprocess/sync/lock_options.hpp> +#include <boost/interprocess/exceptions.hpp> +#include <boost/interprocess/detail/mpl.hpp> +#include <boost/interprocess/detail/type_traits.hpp> +#include <boost/move/move.hpp> +#include <boost/interprocess/detail/posix_time_types_wrk.hpp> + +//!\file +//!Describes the upgradable_lock class that serves to acquire the upgradable +//!lock of a mutex. + +namespace boost { +namespace interprocess { + + +//!sharable_lock is meant to carry out the tasks for sharable-locking +//!(such as read-locking), unlocking, try-sharable-locking and timed-sharable-locking +//!(recursive or not) for the Mutex. The Mutex need not supply all of this +//!functionality. If the client of sharable_lock<Mutex> does not use functionality which +//!the Mutex does not supply, no harm is done. Mutex ownership can be shared among +//!sharable_locks, and a single upgradable_lock. sharable_lock does not support +//!copy semantics. But sharable_lock supports ownership transfer from an sharable_lock, +//!upgradable_lock and scoped_lock via transfer_lock syntax.*/ +template <class SharableMutex> +class sharable_lock +{ + public: + typedef SharableMutex mutex_type; + /// @cond + private: + typedef sharable_lock<SharableMutex> this_type; + explicit sharable_lock(scoped_lock<mutex_type>&); + typedef bool this_type::*unspecified_bool_type; + BOOST_MOVABLE_BUT_NOT_COPYABLE(sharable_lock) + /// @endcond + public: + + //!Effects: Default constructs a sharable_lock. + //!Postconditions: owns() == false and mutex() == 0. + sharable_lock() + : mp_mutex(0), m_locked(false) + {} + + //!Effects: m.lock_sharable(). + //!Postconditions: owns() == true and mutex() == &m. + //!Notes: The constructor will take sharable-ownership of the mutex. If + //! another thread already owns the mutex with exclusive ownership + //! (scoped_lock), this thread will block until the mutex is released. + //! If another thread owns the mutex with sharable or upgradable ownership, + //! then no blocking will occur. Whether or not this constructor handles + //! recursive locking depends upon the mutex. + explicit sharable_lock(mutex_type& m) + : mp_mutex(&m), m_locked(false) + { mp_mutex->lock_sharable(); m_locked = true; } + + //!Postconditions: owns() == false, and mutex() == &m. + //!Notes: The constructor will not take ownership of the mutex. There is no effect + //! required on the referenced mutex. + sharable_lock(mutex_type& m, defer_lock_type) + : mp_mutex(&m), m_locked(false) + {} + + //!Postconditions: owns() == true, and mutex() == &m. + //!Notes: The constructor will suppose that the mutex is already sharable + //! locked. There is no effect required on the referenced mutex. + sharable_lock(mutex_type& m, accept_ownership_type) + : mp_mutex(&m), m_locked(true) + {} + + //!Effects: m.try_lock_sharable() + //!Postconditions: mutex() == &m. owns() == the return value of the + //! m.try_lock_sharable() executed within the constructor. + //!Notes: The constructor will take sharable-ownership of the mutex if it + //! can do so without waiting. Whether or not this constructor handles + //! recursive locking depends upon the mutex. If the mutex_type does not + //! support try_lock_sharable, this constructor will fail at compile + //! time if instantiated, but otherwise have no effect. + sharable_lock(mutex_type& m, try_to_lock_type) + : mp_mutex(&m), m_locked(false) + { m_locked = mp_mutex->try_lock_sharable(); } + + //!Effects: m.timed_lock_sharable(abs_time) + //!Postconditions: mutex() == &m. owns() == the return value of the + //! m.timed_lock_sharable() executed within the constructor. + //!Notes: The constructor will take sharable-ownership of the mutex if it + //! can do so within the time specified. Whether or not this constructor + //! handles recursive locking depends upon the mutex. If the mutex_type + //! does not support timed_lock_sharable, this constructor will fail at + //! compile time if instantiated, but otherwise have no effect. + sharable_lock(mutex_type& m, const boost::posix_time::ptime& abs_time) + : mp_mutex(&m), m_locked(false) + { m_locked = mp_mutex->timed_lock_sharable(abs_time); } + + //!Postconditions: mutex() == upgr.mutex(). owns() == the value of upgr.owns() + //! before the construction. upgr.owns() == false after the construction. + //!Notes: If the upgr sharable_lock owns the mutex, ownership is moved to this + //! sharable_lock with no blocking. If the upgr sharable_lock does not own the mutex, then + //! neither will this sharable_lock. Only a moved sharable_lock's will match this + //! signature. An non-moved sharable_lock can be moved with the expression: + //! "boost::move(lock);". This constructor does not alter the state of the mutex, + //! only potentially who owns it. + sharable_lock(BOOST_RV_REF(sharable_lock<mutex_type>) upgr) + : mp_mutex(0), m_locked(upgr.owns()) + { mp_mutex = upgr.release(); } + + //!Effects: If upgr.owns() then calls unlock_upgradable_and_lock_sharable() on the + //! referenced mutex. + //!Postconditions: mutex() == the value upgr.mutex() had before the construction. + //! upgr.mutex() == 0 owns() == the value of upgr.owns() before construction. + //! upgr.owns() == false after the construction. + //!Notes: If upgr is locked, this constructor will lock this sharable_lock while + //! unlocking upgr. Only a moved sharable_lock's will match this + //! signature. An non-moved upgradable_lock can be moved with the expression: + //! "boost::move(lock);".*/ + template<class T> + sharable_lock(BOOST_RV_REF(upgradable_lock<T>) upgr + , typename ipcdetail::enable_if< ipcdetail::is_same<T, SharableMutex> >::type * = 0) + : mp_mutex(0), m_locked(false) + { + upgradable_lock<mutex_type> &u_lock = upgr; + if(u_lock.owns()){ + u_lock.mutex()->unlock_upgradable_and_lock_sharable(); + m_locked = true; + } + mp_mutex = u_lock.release(); + } + + //!Effects: If scop.owns() then calls unlock_and_lock_sharable() on the + //! referenced mutex. + //!Postconditions: mutex() == the value scop.mutex() had before the construction. + //! scop.mutex() == 0 owns() == scop.owns() before the constructor. After the + //! construction, scop.owns() == false. + //!Notes: If scop is locked, this constructor will transfer the exclusive ownership + //! to a sharable-ownership of this sharable_lock. + //! Only a moved scoped_lock's will match this + //! signature. An non-moved scoped_lock can be moved with the expression: + //! "boost::move(lock);". + template<class T> + sharable_lock(BOOST_RV_REF(scoped_lock<T>) scop + , typename ipcdetail::enable_if< ipcdetail::is_same<T, SharableMutex> >::type * = 0) + : mp_mutex(0), m_locked(false) + { + scoped_lock<mutex_type> &e_lock = scop; + if(e_lock.owns()){ + e_lock.mutex()->unlock_and_lock_sharable(); + m_locked = true; + } + mp_mutex = e_lock.release(); + } + + //!Effects: if (owns()) mp_mutex->unlock_sharable(). + //!Notes: The destructor behavior ensures that the mutex lock is not leaked. + ~sharable_lock() + { + try{ + if(m_locked && mp_mutex) mp_mutex->unlock_sharable(); + } + catch(...){} + } + + //!Effects: If owns() before the call, then unlock_sharable() is called on mutex(). + //! *this gets the state of upgr and upgr gets set to a default constructed state. + //!Notes: With a recursive mutex it is possible that both this and upgr own the mutex + //! before the assignment. In this case, this will own the mutex after the assignment + //! (and upgr will not), but the mutex's lock count will be decremented by one. + sharable_lock &operator=(BOOST_RV_REF(sharable_lock<mutex_type>) upgr) + { + if(this->owns()) + this->unlock(); + m_locked = upgr.owns(); + mp_mutex = upgr.release(); + return *this; + } + + //!Effects: If mutex() == 0 or already locked, throws a lock_exception() + //! exception. Calls lock_sharable() on the referenced mutex. + //!Postconditions: owns() == true. + //!Notes: The sharable_lock changes from a state of not owning the + //! mutex, to owning the mutex, blocking if necessary. + void lock() + { + if(!mp_mutex || m_locked) + throw lock_exception(); + mp_mutex->lock_sharable(); + m_locked = true; + } + + //!Effects: If mutex() == 0 or already locked, throws a lock_exception() + //! exception. Calls try_lock_sharable() on the referenced mutex. + //!Postconditions: owns() == the value returned from + //! mutex()->try_lock_sharable(). + //!Notes: The sharable_lock changes from a state of not owning the mutex, + //! to owning the mutex, but only if blocking was not required. If the + //! mutex_type does not support try_lock_sharable(), this function will + //! fail at compile time if instantiated, but otherwise have no effect. + bool try_lock() + { + if(!mp_mutex || m_locked) + throw lock_exception(); + m_locked = mp_mutex->try_lock_sharable(); + return m_locked; + } + + //!Effects: If mutex() == 0 or already locked, throws a lock_exception() + //! exception. Calls timed_lock_sharable(abs_time) on the referenced mutex. + //!Postconditions: owns() == the value returned from + //! mutex()->timed_lock_sharable(elps_time). + //!Notes: The sharable_lock changes from a state of not owning the mutex, + //! to owning the mutex, but only if it can obtain ownership within the + //! specified time interval. If the mutex_type does not support + //! timed_lock_sharable(), this function will fail at compile time if + //! instantiated, but otherwise have no effect. + bool timed_lock(const boost::posix_time::ptime& abs_time) + { + if(!mp_mutex || m_locked) + throw lock_exception(); + m_locked = mp_mutex->timed_lock_sharable(abs_time); + return m_locked; + } + + //!Effects: If mutex() == 0 or not locked, throws a lock_exception() exception. + //! Calls unlock_sharable() on the referenced mutex. + //!Postconditions: owns() == false. + //!Notes: The sharable_lock changes from a state of owning the mutex, to + //! not owning the mutex. + void unlock() + { + if(!mp_mutex || !m_locked) + throw lock_exception(); + mp_mutex->unlock_sharable(); + m_locked = false; + } + + //!Effects: Returns true if this scoped_lock has + //!acquired the referenced mutex. + bool owns() const + { return m_locked && mp_mutex; } + + //!Conversion to bool. + //!Returns owns(). + operator unspecified_bool_type() const + { return m_locked? &this_type::m_locked : 0; } + + //!Effects: Returns a pointer to the referenced mutex, or 0 if + //!there is no mutex to reference. + mutex_type* mutex() const + { return mp_mutex; } + + //!Effects: Returns a pointer to the referenced mutex, or 0 if there is no + //! mutex to reference. + //!Postconditions: mutex() == 0 and owns() == false. + mutex_type* release() + { + mutex_type *mut = mp_mutex; + mp_mutex = 0; + m_locked = false; + return mut; + } + + //!Effects: Swaps state with moved lock. + //!Throws: Nothing. + void swap(sharable_lock<mutex_type> &other) + { + std::swap(mp_mutex, other.mp_mutex); + std::swap(m_locked, other.m_locked); + } + + /// @cond + private: + mutex_type *mp_mutex; + bool m_locked; + /// @endcond +}; + +} // namespace interprocess +} // namespace boost + +#include <boost/interprocess/detail/config_end.hpp> + +#endif // BOOST_INTERPROCESS_SHARABLE_LOCK_HPP diff --git a/boost/interprocess/sync/shm/named_condition.hpp b/boost/interprocess/sync/shm/named_condition.hpp new file mode 100644 index 0000000000..0d67c25757 --- /dev/null +++ b/boost/interprocess/sync/shm/named_condition.hpp @@ -0,0 +1,379 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2005-2011. 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_SHM_NAMED_CONDITION_HPP +#define BOOST_INTERPROCESS_SHM_NAMED_CONDITION_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/static_assert.hpp> +#include <boost/interprocess/detail/type_traits.hpp> +#include <boost/interprocess/creation_tags.hpp> +#include <boost/interprocess/exceptions.hpp> +#include <boost/interprocess/shared_memory_object.hpp> +#include <boost/interprocess/sync/interprocess_condition.hpp> +#include <boost/interprocess/detail/managed_open_or_create_impl.hpp> +#include <boost/interprocess/detail/posix_time_types_wrk.hpp> +#include <boost/interprocess/sync/shm/named_creation_functor.hpp> +#include <boost/interprocess/sync/named_mutex.hpp> +#include <boost/interprocess/permissions.hpp> +#if defined BOOST_INTERPROCESS_NAMED_MUTEX_USES_POSIX_SEMAPHORES +#include <boost/interprocess/sync/interprocess_mutex.hpp> +#include <boost/interprocess/sync/scoped_lock.hpp> +#endif + + +//!\file +//!Describes process-shared variables interprocess_condition class + +namespace boost { +namespace interprocess { +namespace ipcdetail { + +/// @cond +namespace ipcdetail{ class interprocess_tester; } +/// @endcond + +//! A global condition variable that can be created by name. +//! This condition variable is designed to work with named_mutex and +//! can't be placed in shared memory or memory mapped files. +class shm_named_condition +{ + /// @cond + //Non-copyable + shm_named_condition(); + shm_named_condition(const shm_named_condition &); + shm_named_condition &operator=(const shm_named_condition &); + /// @endcond + public: + //!Creates a global condition with a name. + //!If the condition can't be created throws interprocess_exception + shm_named_condition(create_only_t create_only, const char *name, const permissions &perm = permissions()); + + //!Opens or creates a global condition with a name. + //!If the condition is created, this call is equivalent to + //!shm_named_condition(create_only_t, ... ) + //!If the condition is already created, this call is equivalent + //!shm_named_condition(open_only_t, ... ) + //!Does not throw + shm_named_condition(open_or_create_t open_or_create, const char *name, const permissions &perm = permissions()); + + //!Opens a global condition with a name if that condition is previously + //!created. If it is not previously created this function throws + //!interprocess_exception. + shm_named_condition(open_only_t open_only, const char *name); + + //!Destroys *this and indicates that the calling process is finished using + //!the resource. The destructor function will deallocate + //!any system resources allocated by the system for use by this process for + //!this resource. The resource can still be opened again calling + //!the open constructor overload. To erase the resource from the system + //!use remove(). + ~shm_named_condition(); + + //!If there is a thread waiting on *this, change that + //!thread's state to ready. Otherwise there is no effect.*/ + void notify_one(); + + //!Change the state of all threads waiting on *this to ready. + //!If there are no waiting threads, notify_all() has no effect. + void notify_all(); + + //!Releases the lock on the named_mutex object associated with lock, blocks + //!the current thread of execution until readied by a call to + //!this->notify_one() or this->notify_all(), and then reacquires the lock. + template <typename L> + void wait(L& lock); + + //!The same as: + //!while (!pred()) wait(lock) + template <typename L, typename Pr> + void wait(L& lock, Pr pred); + + //!Releases the lock on the named_mutex object associated with lock, blocks + //!the current thread of execution until readied by a call to + //!this->notify_one() or this->notify_all(), or until time abs_time is reached, + //!and then reacquires the lock. + //!Returns: false if time abs_time is reached, otherwise true. + template <typename L> + bool timed_wait(L& lock, const boost::posix_time::ptime &abs_time); + + //!The same as: while (!pred()) { + //! if (!timed_wait(lock, abs_time)) return pred(); + //! } return true; + template <typename L, typename Pr> + bool timed_wait(L& lock, const boost::posix_time::ptime &abs_time, Pr pred); + + //!Erases a named condition from the system. + //!Returns false on error. Never throws. + static bool remove(const char *name); + + /// @cond + private: + + struct condition_holder + { + interprocess_condition cond_; + //If named_mutex is implemented using semaphores + //we need to store an additional mutex + #if defined (BOOST_INTERPROCESS_NAMED_MUTEX_USES_POSIX_SEMAPHORES) + interprocess_mutex mutex_; + #endif + }; + + interprocess_condition *condition() const + { return &static_cast<condition_holder*>(m_shmem.get_user_address())->cond_; } + + template <class Lock> + class lock_inverter + { + Lock &l_; + public: + lock_inverter(Lock &l) + : l_(l) + {} + void lock() { l_.unlock(); } + void unlock() { l_.lock(); } + }; + + #if defined (BOOST_INTERPROCESS_NAMED_MUTEX_USES_POSIX_SEMAPHORES) + interprocess_mutex *mutex() const + { return &static_cast<condition_holder*>(m_shmem.get_user_address())->mutex_; } + + template <class Lock> + void do_wait(Lock& lock) + { + //shm_named_condition only works with named_mutex + BOOST_STATIC_ASSERT((is_convertible<typename Lock::mutex_type&, named_mutex&>::value == true)); + + //lock internal before unlocking external to avoid race with a notifier + scoped_lock<interprocess_mutex> internal_lock(*this->mutex()); + lock_inverter<Lock> inverted_lock(lock); + scoped_lock<lock_inverter<Lock> > external_unlock(inverted_lock); + + //unlock internal first to avoid deadlock with near simultaneous waits + scoped_lock<interprocess_mutex> internal_unlock; + internal_lock.swap(internal_unlock); + this->condition()->wait(internal_unlock); + } + + template <class Lock> + bool do_timed_wait(Lock& lock, const boost::posix_time::ptime &abs_time) + { + //shm_named_condition only works with named_mutex + BOOST_STATIC_ASSERT((is_convertible<typename Lock::mutex_type&, named_mutex&>::value == true)); + //lock internal before unlocking external to avoid race with a notifier + scoped_lock<interprocess_mutex> internal_lock(*this->mutex(), abs_time); + if(!internal_lock) return false; + lock_inverter<Lock> inverted_lock(lock); + scoped_lock<lock_inverter<Lock> > external_unlock(inverted_lock); + + //unlock internal first to avoid deadlock with near simultaneous waits + scoped_lock<interprocess_mutex> internal_unlock; + internal_lock.swap(internal_unlock); + return this->condition()->timed_wait(internal_unlock, abs_time); + } + #else + template<class Lock> + class lock_wrapper + { + typedef void (lock_wrapper::*unspecified_bool_type)(); + public: + + typedef interprocess_mutex mutex_type; + + lock_wrapper(Lock &l) + : l_(l) + {} + + mutex_type* mutex() const + { return l_.mutex()->mutex(); } + + void lock() { l_.lock(); } + + void unlock() { l_.unlock(); } + + operator unspecified_bool_type() const + { return l_ ? &lock_wrapper::lock : 0; } + + private: + Lock &l_; + }; + #endif + + friend class boost::interprocess::ipcdetail::interprocess_tester; + void dont_close_on_destruction(); + + managed_open_or_create_impl<shared_memory_object> m_shmem; + + template <class T, class Arg> friend class boost::interprocess::ipcdetail::named_creation_functor; + typedef boost::interprocess::ipcdetail::named_creation_functor<condition_holder> construct_func_t; + /// @endcond +}; + +/// @cond + +inline shm_named_condition::~shm_named_condition() +{} + +inline shm_named_condition::shm_named_condition(create_only_t, const char *name, const permissions &perm) + : m_shmem (create_only + ,name + ,sizeof(condition_holder) + + managed_open_or_create_impl<shared_memory_object>:: + ManagedOpenOrCreateUserOffset + ,read_write + ,0 + ,construct_func_t(DoCreate) + ,perm) +{} + +inline shm_named_condition::shm_named_condition(open_or_create_t, const char *name, const permissions &perm) + : m_shmem (open_or_create + ,name + ,sizeof(condition_holder) + + managed_open_or_create_impl<shared_memory_object>:: + ManagedOpenOrCreateUserOffset + ,read_write + ,0 + ,construct_func_t(DoOpenOrCreate) + ,perm) +{} + +inline shm_named_condition::shm_named_condition(open_only_t, const char *name) + : m_shmem (open_only + ,name + ,read_write + ,0 + ,construct_func_t(DoOpen)) +{} + +inline void shm_named_condition::dont_close_on_destruction() +{ interprocess_tester::dont_close_on_destruction(m_shmem); } + +#if defined(BOOST_INTERPROCESS_NAMED_MUTEX_USES_POSIX_SEMAPHORES) + +inline void shm_named_condition::notify_one() +{ + scoped_lock<interprocess_mutex> internal_lock(*this->mutex()); + this->condition()->notify_one(); +} + +inline void shm_named_condition::notify_all() +{ + scoped_lock<interprocess_mutex> internal_lock(*this->mutex()); + this->condition()->notify_all(); +} + +template <typename L> +inline void shm_named_condition::wait(L& lock) +{ + if (!lock) + throw lock_exception(); + this->do_wait(lock); +} + +template <typename L, typename Pr> +inline void shm_named_condition::wait(L& lock, Pr pred) +{ + if (!lock) + throw lock_exception(); + while (!pred()) + this->do_wait(lock); +} + +template <typename L> +inline bool shm_named_condition::timed_wait + (L& lock, const boost::posix_time::ptime &abs_time) +{ + if(abs_time == boost::posix_time::pos_infin){ + this->wait(lock); + return true; + } + if (!lock) + throw lock_exception(); + return this->do_timed_wait(lock, abs_time); +} + +template <typename L, typename Pr> +inline bool shm_named_condition::timed_wait + (L& lock, const boost::posix_time::ptime &abs_time, Pr pred) +{ + if(abs_time == boost::posix_time::pos_infin){ + this->wait(lock, pred); + return true; + } + if (!lock) + throw lock_exception(); + + while (!pred()){ + if(!this->do_timed_wait(lock, abs_time)){ + return pred(); + } + } + return true; +} + +#else + +inline void shm_named_condition::notify_one() +{ this->condition()->notify_one(); } + +inline void shm_named_condition::notify_all() +{ this->condition()->notify_all(); } + +template <typename L> +inline void shm_named_condition::wait(L& lock) +{ + lock_wrapper<L> newlock(lock); + this->condition()->wait(newlock); +} + +template <typename L, typename Pr> +inline void shm_named_condition::wait(L& lock, Pr pred) +{ + lock_wrapper<L> newlock(lock); + this->condition()->wait(newlock, pred); +} + +template <typename L> +inline bool shm_named_condition::timed_wait + (L& lock, const boost::posix_time::ptime &abs_time) +{ + lock_wrapper<L> newlock(lock); + return this->condition()->timed_wait(newlock, abs_time); +} + +template <typename L, typename Pr> +inline bool shm_named_condition::timed_wait + (L& lock, const boost::posix_time::ptime &abs_time, Pr pred) +{ + lock_wrapper<L> newlock(lock); + return this->condition()->timed_wait(newlock, abs_time, pred); +} + +#endif + +inline bool shm_named_condition::remove(const char *name) +{ return shared_memory_object::remove(name); } + +/// @endcond + +} //namespace ipcdetail +} //namespace interprocess +} //namespace boost + +#include <boost/interprocess/detail/config_end.hpp> + +#endif // BOOST_INTERPROCESS_SHM_NAMED_CONDITION_HPP diff --git a/boost/interprocess/sync/shm/named_creation_functor.hpp b/boost/interprocess/sync/shm/named_creation_functor.hpp new file mode 100644 index 0000000000..11a1db1d6f --- /dev/null +++ b/boost/interprocess/sync/shm/named_creation_functor.hpp @@ -0,0 +1,68 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2007-2011. 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_SYNC_NAMED_CREATION_FUNCTOR_HPP +#define BOOST_INTERPROCESS_SYNC_NAMED_CREATION_FUNCTOR_HPP + +#include <boost/interprocess/creation_tags.hpp> +#include <boost/interprocess/detail/type_traits.hpp> +#include <boost/interprocess/detail/mpl.hpp> + +namespace boost { +namespace interprocess { +namespace ipcdetail { + +struct named_creation_functor_no_arg{}; + +template <class T, class Arg = named_creation_functor_no_arg> +class named_creation_functor +{ + typedef named_creation_functor_no_arg no_arg_t; + public: + named_creation_functor(create_enum_t type, Arg arg = Arg()) + : m_creation_type(type), m_arg(arg){} + + template<class ArgType> + void construct(void *address, typename enable_if_c<is_same<ArgType, no_arg_t>::value>::type * = 0) const + { new(address)T; } + + template<class ArgType> + void construct(void *address, typename enable_if_c<!is_same<ArgType, no_arg_t>::value>::type * = 0) const + { new(address)T(m_arg); } + + bool operator()(void *address, std::size_t, bool created) const + { + switch(m_creation_type){ + case DoOpen: + return true; + break; + case DoCreate: + case DoOpenOrCreate: + if(created){ + construct<Arg>(address); + } + return true; + break; + + default: + return false; + break; + } + } + private: + create_enum_t m_creation_type; + Arg m_arg; +}; + +} //namespace ipcdetail { +} //namespace interprocess { +} //namespace boost { + +#endif //BOOST_INTERPROCESS_SYNC_NAMED_CREATION_FUNCTOR_HPP diff --git a/boost/interprocess/sync/shm/named_mutex.hpp b/boost/interprocess/sync/shm/named_mutex.hpp new file mode 100644 index 0000000000..a71eb4fe68 --- /dev/null +++ b/boost/interprocess/sync/shm/named_mutex.hpp @@ -0,0 +1,184 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2005-2011. 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_SHM_NAMED_MUTEX_HPP +#define BOOST_INTERPROCESS_SHM_NAMED_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/creation_tags.hpp> +#include <boost/interprocess/exceptions.hpp> +#include <boost/interprocess/detail/interprocess_tester.hpp> +#include <boost/interprocess/permissions.hpp> +#include <boost/interprocess/detail/posix_time_types_wrk.hpp> + +#include <boost/interprocess/shared_memory_object.hpp> +#include <boost/interprocess/sync/interprocess_mutex.hpp> +#include <boost/interprocess/detail/managed_open_or_create_impl.hpp> +#include <boost/interprocess/sync/shm/named_creation_functor.hpp> + +//!\file +//!Describes a named mutex class for inter-process synchronization + +namespace boost { +namespace interprocess { +namespace ipcdetail { + +class named_condition; + +//!A mutex with a global name, so it can be found from different +//!processes. This mutex can't be placed in shared memory, and +//!each process should have it's own named mutex. +class shm_named_mutex +{ + /// @cond + + //Non-copyable + shm_named_mutex(); + shm_named_mutex(const shm_named_mutex &); + shm_named_mutex &operator=(const shm_named_mutex &); + friend class named_condition; + /// @endcond + + public: + //!Creates a global interprocess_mutex with a name. + //!Throws interprocess_exception on error. + shm_named_mutex(create_only_t create_only, const char *name, const permissions &perm = permissions()); + + //!Opens or creates a global mutex with a name. + //!If the mutex is created, this call is equivalent to + //!shm_named_mutex(create_only_t, ... ) + //!If the mutex is already created, this call is equivalent + //!shm_named_mutex(open_only_t, ... ) + //!Does not throw + shm_named_mutex(open_or_create_t open_or_create, const char *name, const permissions &perm = permissions()); + + //!Opens a global mutex with a name if that mutex is previously + //!created. If it is not previously created this function throws + //!interprocess_exception. + shm_named_mutex(open_only_t open_only, const char *name); + + //!Destroys *this and indicates that the calling process is finished using + //!the resource. The destructor function will deallocate + //!any system resources allocated by the system for use by this process for + //!this resource. The resource can still be opened again calling + //!the open constructor overload. To erase the resource from the system + //!use remove(). + ~shm_named_mutex(); + + //!Unlocks a previously locked + //!interprocess_mutex. + void unlock(); + + //!Locks interprocess_mutex, sleeps when interprocess_mutex is already locked. + //!Throws interprocess_exception if a severe error is found + void lock(); + + //!Tries to lock the interprocess_mutex, returns false when interprocess_mutex + //!is already locked, returns true when success. + //!Throws interprocess_exception if a severe error is found + bool try_lock(); + + //!Tries to lock the interprocess_mutex until time abs_time, + //!Returns false when timeout expires, returns true when locks. + //!Throws interprocess_exception if a severe error is found + bool timed_lock(const boost::posix_time::ptime &abs_time); + + //!Erases a named mutex from the system. + //!Returns false on error. Never throws. + static bool remove(const char *name); + + /// @cond + interprocess_mutex *mutex() const + { return static_cast<interprocess_mutex*>(m_shmem.get_user_address()); } + + private: + friend class ipcdetail::interprocess_tester; + void dont_close_on_destruction(); + + ipcdetail::managed_open_or_create_impl<shared_memory_object> m_shmem; + typedef ipcdetail::named_creation_functor<interprocess_mutex> construct_func_t; + /// @endcond +}; + +/// @cond + +inline void shm_named_mutex::dont_close_on_destruction() +{ ipcdetail::interprocess_tester::dont_close_on_destruction(m_shmem); } + +inline shm_named_mutex::~shm_named_mutex() +{} + +inline shm_named_mutex::shm_named_mutex(create_only_t, const char *name, const permissions &perm) + : m_shmem (create_only + ,name + ,sizeof(interprocess_mutex) + + ipcdetail::managed_open_or_create_impl<shared_memory_object>:: + ManagedOpenOrCreateUserOffset + ,read_write + ,0 + ,construct_func_t(ipcdetail::DoCreate) + ,perm) +{} + +inline shm_named_mutex::shm_named_mutex(open_or_create_t, const char *name, const permissions &perm) + : m_shmem (open_or_create + ,name + ,sizeof(interprocess_mutex) + + ipcdetail::managed_open_or_create_impl<shared_memory_object>:: + ManagedOpenOrCreateUserOffset + ,read_write + ,0 + ,construct_func_t(ipcdetail::DoOpenOrCreate) + ,perm) +{} + +inline shm_named_mutex::shm_named_mutex(open_only_t, const char *name) + : m_shmem (open_only + ,name + ,read_write + ,0 + ,construct_func_t(ipcdetail::DoOpen)) +{} + +inline void shm_named_mutex::lock() +{ this->mutex()->lock(); } + +inline void shm_named_mutex::unlock() +{ this->mutex()->unlock(); } + +inline bool shm_named_mutex::try_lock() +{ return this->mutex()->try_lock(); } + +inline bool shm_named_mutex::timed_lock(const boost::posix_time::ptime &abs_time) +{ + if(abs_time == boost::posix_time::pos_infin){ + this->lock(); + return true; + } + return this->mutex()->timed_lock(abs_time); +} + +inline bool shm_named_mutex::remove(const char *name) +{ return shared_memory_object::remove(name); } + +/// @endcond + +} //namespace ipcdetail { +} //namespace interprocess { +} //namespace boost { + +#include <boost/interprocess/detail/config_end.hpp> + +#endif //BOOST_INTERPROCESS_SHM_NAMED_MUTEX_HPP diff --git a/boost/interprocess/sync/shm/named_recursive_mutex.hpp b/boost/interprocess/sync/shm/named_recursive_mutex.hpp new file mode 100644 index 0000000000..461c97eb32 --- /dev/null +++ b/boost/interprocess/sync/shm/named_recursive_mutex.hpp @@ -0,0 +1,175 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2005-2011. 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_SHM_NAMED_RECURSIVE_MUTEX_HPP +#define BOOST_INTERPROCESS_SHM_NAMED_RECURSIVE_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/creation_tags.hpp> +#include <boost/interprocess/exceptions.hpp> +#include <boost/interprocess/detail/posix_time_types_wrk.hpp> +#include <boost/interprocess/shared_memory_object.hpp> +#include <boost/interprocess/detail/managed_open_or_create_impl.hpp> +#include <boost/interprocess/sync/interprocess_recursive_mutex.hpp> +#include <boost/interprocess/sync/shm/named_creation_functor.hpp> +#include <boost/interprocess/permissions.hpp> + +//!\file +//!Describes a named shm_named_recursive_mutex class for inter-process synchronization + +namespace boost { +namespace interprocess { +namespace ipcdetail { + +/// @cond +class interprocess_tester; +/// @endcond + +class shm_named_recursive_mutex +{ + /// @cond + //Non-copyable + shm_named_recursive_mutex(); + shm_named_recursive_mutex(const shm_named_recursive_mutex &); + shm_named_recursive_mutex &operator=(const shm_named_recursive_mutex &); + /// @endcond + public: + + //!Creates a global recursive_mutex with a name. + //!If the recursive_mutex can't be created throws interprocess_exception + shm_named_recursive_mutex(create_only_t create_only, const char *name, const permissions &perm = permissions()); + + //!Opens or creates a global recursive_mutex with a name. + //!If the recursive_mutex is created, this call is equivalent to + //!shm_named_recursive_mutex(create_only_t, ... ) + //!If the recursive_mutex is already created, this call is equivalent + //!shm_named_recursive_mutex(open_only_t, ... ) + //!Does not throw + shm_named_recursive_mutex(open_or_create_t open_or_create, const char *name, const permissions &perm = permissions()); + + //!Opens a global recursive_mutex with a name if that recursive_mutex is previously + //!created. If it is not previously created this function throws + //!interprocess_exception. + shm_named_recursive_mutex(open_only_t open_only, const char *name); + + //!Destroys *this and indicates that the calling process is finished using + //!the resource. The destructor function will deallocate + //!any system resources allocated by the system for use by this process for + //!this resource. The resource can still be opened again calling + //!the open constructor overload. To erase the resource from the system + //!use remove(). + ~shm_named_recursive_mutex(); + + //!Unlocks a previously locked + //!shm_named_recursive_mutex. + void unlock(); + + //!Locks shm_named_recursive_mutex, sleeps when shm_named_recursive_mutex is already locked. + //!Throws interprocess_exception if a severe error is found. + void lock(); + + //!Tries to lock the shm_named_recursive_mutex, returns false when shm_named_recursive_mutex + //!is already locked, returns true when success. + //!Throws interprocess_exception if a severe error is found. + bool try_lock(); + + //!Tries to lock the shm_named_recursive_mutex until time abs_time, + //!Returns false when timeout expires, returns true when locks. + //!Throws interprocess_exception if a severe error is found + bool timed_lock(const boost::posix_time::ptime &abs_time); + + //!Erases a named recursive mutex + //!from the system + static bool remove(const char *name); + + /// @cond + private: + friend class interprocess_tester; + void dont_close_on_destruction(); + + interprocess_recursive_mutex *mutex() const + { return static_cast<interprocess_recursive_mutex*>(m_shmem.get_user_address()); } + + managed_open_or_create_impl<shared_memory_object> m_shmem; + typedef named_creation_functor<interprocess_recursive_mutex> construct_func_t; + /// @endcond +}; + +inline shm_named_recursive_mutex::~shm_named_recursive_mutex() +{} + +inline void shm_named_recursive_mutex::dont_close_on_destruction() +{ interprocess_tester::dont_close_on_destruction(m_shmem); } + +inline shm_named_recursive_mutex::shm_named_recursive_mutex(create_only_t, const char *name, const permissions &perm) + : m_shmem (create_only + ,name + ,sizeof(interprocess_recursive_mutex) + + managed_open_or_create_impl<shared_memory_object>:: + ManagedOpenOrCreateUserOffset + ,read_write + ,0 + ,construct_func_t(DoCreate) + ,perm) +{} + +inline shm_named_recursive_mutex::shm_named_recursive_mutex(open_or_create_t, const char *name, const permissions &perm) + : m_shmem (open_or_create + ,name + ,sizeof(interprocess_recursive_mutex) + + managed_open_or_create_impl<shared_memory_object>:: + ManagedOpenOrCreateUserOffset + ,read_write + ,0 + ,construct_func_t(DoOpenOrCreate) + ,perm) +{} + +inline shm_named_recursive_mutex::shm_named_recursive_mutex(open_only_t, const char *name) + : m_shmem (open_only + ,name + ,read_write + ,0 + ,construct_func_t(DoOpen)) +{} + +inline void shm_named_recursive_mutex::lock() +{ this->mutex()->lock(); } + +inline void shm_named_recursive_mutex::unlock() +{ this->mutex()->unlock(); } + +inline bool shm_named_recursive_mutex::try_lock() +{ return this->mutex()->try_lock(); } + +inline bool shm_named_recursive_mutex::timed_lock(const boost::posix_time::ptime &abs_time) +{ + if(abs_time == boost::posix_time::pos_infin){ + this->lock(); + return true; + } + return this->mutex()->timed_lock(abs_time); +} + +inline bool shm_named_recursive_mutex::remove(const char *name) +{ return shared_memory_object::remove(name); } + +} //namespace ipcdetail { +} //namespace interprocess { +} //namespace boost { + +#include <boost/interprocess/detail/config_end.hpp> + +#endif //BOOST_INTERPROCESS_SHM_NAMED_RECURSIVE_MUTEX_HPP diff --git a/boost/interprocess/sync/shm/named_semaphore.hpp b/boost/interprocess/sync/shm/named_semaphore.hpp new file mode 100644 index 0000000000..c6d3830db5 --- /dev/null +++ b/boost/interprocess/sync/shm/named_semaphore.hpp @@ -0,0 +1,141 @@ + ////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2005-2011. 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_SHM_NAMED_SEMAPHORE_HPP +#define BOOST_INTERPROCESS_SHM_NAMED_SEMAPHORE_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/creation_tags.hpp> +#include <boost/interprocess/exceptions.hpp> +#include <boost/interprocess/permissions.hpp> +#include <boost/interprocess/detail/interprocess_tester.hpp> +#include <boost/interprocess/detail/posix_time_types_wrk.hpp> +#include <boost/interprocess/shared_memory_object.hpp> +#include <boost/interprocess/detail/managed_open_or_create_impl.hpp> +#include <boost/interprocess/sync/interprocess_semaphore.hpp> +#include <boost/interprocess/sync/shm/named_creation_functor.hpp> + +namespace boost { +namespace interprocess { +namespace ipcdetail { + +class shm_named_semaphore +{ + /// @cond + + //Non-copyable + shm_named_semaphore(); + shm_named_semaphore(const shm_named_semaphore &); + shm_named_semaphore &operator=(const shm_named_semaphore &); + /// @endcond + + public: + shm_named_semaphore(create_only_t, const char *name, unsigned int initialCount, const permissions &perm = permissions()); + + shm_named_semaphore(open_or_create_t, const char *name, unsigned int initialCount, const permissions &perm = permissions()); + + shm_named_semaphore(open_only_t, const char *name); + + ~shm_named_semaphore(); + + void post(); + void wait(); + bool try_wait(); + bool timed_wait(const boost::posix_time::ptime &abs_time); + + static bool remove(const char *name); + + /// @cond + private: + friend class interprocess_tester; + void dont_close_on_destruction(); + + interprocess_semaphore *semaphore() const + { return static_cast<interprocess_semaphore*>(m_shmem.get_user_address()); } + + managed_open_or_create_impl<shared_memory_object> m_shmem; + typedef named_creation_functor<interprocess_semaphore, int> construct_func_t; + /// @endcond +}; + +inline shm_named_semaphore::~shm_named_semaphore() +{} + +inline void shm_named_semaphore::dont_close_on_destruction() +{ interprocess_tester::dont_close_on_destruction(m_shmem); } + +inline shm_named_semaphore::shm_named_semaphore + (create_only_t, const char *name, unsigned int initialCount, const permissions &perm) + : m_shmem (create_only + ,name + ,sizeof(interprocess_semaphore) + + managed_open_or_create_impl<shared_memory_object>:: + ManagedOpenOrCreateUserOffset + ,read_write + ,0 + ,construct_func_t(DoCreate, initialCount) + ,perm) +{} + +inline shm_named_semaphore::shm_named_semaphore + (open_or_create_t, const char *name, unsigned int initialCount, const permissions &perm) + : m_shmem (open_or_create + ,name + ,sizeof(interprocess_semaphore) + + managed_open_or_create_impl<shared_memory_object>:: + ManagedOpenOrCreateUserOffset + ,read_write + ,0 + ,construct_func_t(DoOpenOrCreate, initialCount) + ,perm) +{} + +inline shm_named_semaphore::shm_named_semaphore + (open_only_t, const char *name) + : m_shmem (open_only + ,name + ,read_write + ,0 + ,construct_func_t(DoOpen, 0)) +{} + +inline void shm_named_semaphore::post() +{ semaphore()->post(); } + +inline void shm_named_semaphore::wait() +{ semaphore()->wait(); } + +inline bool shm_named_semaphore::try_wait() +{ return semaphore()->try_wait(); } + +inline bool shm_named_semaphore::timed_wait(const boost::posix_time::ptime &abs_time) +{ + if(abs_time == boost::posix_time::pos_infin){ + this->wait(); + return true; + } + return semaphore()->timed_wait(abs_time); +} + +inline bool shm_named_semaphore::remove(const char *name) +{ return shared_memory_object::remove(name); } + +} //namespace ipcdetail { +} //namespace interprocess { +} //namespace boost { + +#include <boost/interprocess/detail/config_end.hpp> + +#endif //BOOST_INTERPROCESS_SHM_NAMED_SEMAPHORE_HPP diff --git a/boost/interprocess/sync/shm/named_upgradable_mutex.hpp b/boost/interprocess/sync/shm/named_upgradable_mutex.hpp new file mode 100644 index 0000000000..338fa98f7d --- /dev/null +++ b/boost/interprocess/sync/shm/named_upgradable_mutex.hpp @@ -0,0 +1,372 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2005-2011. 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_NAMED_UPGRADABLE_MUTEX_HPP +#define BOOST_INTERPROCESS_NAMED_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/creation_tags.hpp> +#include <boost/interprocess/exceptions.hpp> +#include <boost/interprocess/shared_memory_object.hpp> +#include <boost/interprocess/detail/managed_open_or_create_impl.hpp> +#include <boost/interprocess/sync/interprocess_upgradable_mutex.hpp> +#include <boost/interprocess/detail/posix_time_types_wrk.hpp> +#include <boost/interprocess/sync/shm/named_creation_functor.hpp> +#include <boost/interprocess/permissions.hpp> + +//!\file +//!Describes a named upgradable mutex class for inter-process synchronization + +namespace boost { +namespace interprocess { + +/// @cond +namespace ipcdetail{ class interprocess_tester; } +/// @endcond + +class named_condition; + +//!A upgradable mutex with a global name, so it can be found from different +//!processes. This mutex can't be placed in shared memory, and +//!each process should have it's own named upgradable mutex. +class named_upgradable_mutex +{ + /// @cond + //Non-copyable + named_upgradable_mutex(); + named_upgradable_mutex(const named_upgradable_mutex &); + named_upgradable_mutex &operator=(const named_upgradable_mutex &); + friend class named_condition; + /// @endcond + public: + + //!Creates a global upgradable mutex with a name. + //!If the upgradable mutex can't be created throws interprocess_exception + named_upgradable_mutex(create_only_t create_only, const char *name, const permissions &perm = permissions()); + + //!Opens or creates a global upgradable mutex with a name, and an initial count. + //!If the upgradable mutex is created, this call is equivalent to + //!named_upgradable_mutex(create_only_t, ...) + //!If the upgradable mutex is already created, this call is equivalent to + //!named_upgradable_mutex(open_only_t, ... ). + named_upgradable_mutex(open_or_create_t open_or_create, const char *name, const permissions &perm = permissions()); + + //!Opens a global upgradable mutex with a name if that upgradable mutex + //!is previously. + //!created. If it is not previously created this function throws + //!interprocess_exception. + named_upgradable_mutex(open_only_t open_only, const char *name); + + //!Destroys *this and indicates that the calling process is finished using + //!the resource. The destructor function will deallocate + //!any system resources allocated by the system for use by this process for + //!this resource. The resource can still be opened again calling + //!the open constructor overload. To erase the resource from the system + //!use remove(). + ~named_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 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 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 exclusive 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 exclusive + //! 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 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 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 it. + //!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(); + + bool try_unlock_sharable_and_lock_upgradable(); + + //!Erases a named upgradable mutex from the system. + //!Returns false on error. Never throws. + static bool remove(const char *name); + + /// @cond + private: + friend class ipcdetail::interprocess_tester; + void dont_close_on_destruction(); + + interprocess_upgradable_mutex *mutex() const + { return static_cast<interprocess_upgradable_mutex*>(m_shmem.get_user_address()); } + + ipcdetail::managed_open_or_create_impl<shared_memory_object> m_shmem; + typedef ipcdetail::named_creation_functor<interprocess_upgradable_mutex> construct_func_t; + /// @endcond +}; + +/// @cond + +inline named_upgradable_mutex::~named_upgradable_mutex() +{} + +inline named_upgradable_mutex::named_upgradable_mutex + (create_only_t, const char *name, const permissions &perm) + : m_shmem (create_only + ,name + ,sizeof(interprocess_upgradable_mutex) + + ipcdetail::managed_open_or_create_impl<shared_memory_object>:: + ManagedOpenOrCreateUserOffset + ,read_write + ,0 + ,construct_func_t(ipcdetail::DoCreate) + ,perm) +{} + +inline named_upgradable_mutex::named_upgradable_mutex + (open_or_create_t, const char *name, const permissions &perm) + : m_shmem (open_or_create + ,name + ,sizeof(interprocess_upgradable_mutex) + + ipcdetail::managed_open_or_create_impl<shared_memory_object>:: + ManagedOpenOrCreateUserOffset + ,read_write + ,0 + ,construct_func_t(ipcdetail::DoOpenOrCreate) + ,perm) +{} + +inline named_upgradable_mutex::named_upgradable_mutex + (open_only_t, const char *name) + : m_shmem (open_only + ,name + ,read_write + ,0 + ,construct_func_t(ipcdetail::DoOpen)) +{} + +inline void named_upgradable_mutex::dont_close_on_destruction() +{ ipcdetail::interprocess_tester::dont_close_on_destruction(m_shmem); } + +inline void named_upgradable_mutex::lock() +{ this->mutex()->lock(); } + +inline void named_upgradable_mutex::unlock() +{ this->mutex()->unlock(); } + +inline bool named_upgradable_mutex::try_lock() +{ return this->mutex()->try_lock(); } + +inline bool named_upgradable_mutex::timed_lock + (const boost::posix_time::ptime &abs_time) +{ + if(abs_time == boost::posix_time::pos_infin){ + this->lock(); + return true; + } + return this->mutex()->timed_lock(abs_time); +} + +inline void named_upgradable_mutex::lock_upgradable() +{ this->mutex()->lock_upgradable(); } + +inline void named_upgradable_mutex::unlock_upgradable() +{ this->mutex()->unlock_upgradable(); } + +inline bool named_upgradable_mutex::try_lock_upgradable() +{ return this->mutex()->try_lock_upgradable(); } + +inline bool named_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; + } + return this->mutex()->timed_lock_upgradable(abs_time); +} + +inline void named_upgradable_mutex::lock_sharable() +{ this->mutex()->lock_sharable(); } + +inline void named_upgradable_mutex::unlock_sharable() +{ this->mutex()->unlock_sharable(); } + +inline bool named_upgradable_mutex::try_lock_sharable() +{ return this->mutex()->try_lock_sharable(); } + +inline bool named_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; + } + return this->mutex()->timed_lock_sharable(abs_time); +} + +inline void named_upgradable_mutex::unlock_and_lock_upgradable() +{ this->mutex()->unlock_and_lock_upgradable(); } + +inline void named_upgradable_mutex::unlock_and_lock_sharable() +{ this->mutex()->unlock_and_lock_sharable(); } + +inline void named_upgradable_mutex::unlock_upgradable_and_lock_sharable() +{ this->mutex()->unlock_upgradable_and_lock_sharable(); } + +inline void named_upgradable_mutex::unlock_upgradable_and_lock() +{ this->mutex()->unlock_upgradable_and_lock(); } + +inline bool named_upgradable_mutex::try_unlock_upgradable_and_lock() +{ return this->mutex()->try_unlock_upgradable_and_lock(); } + +inline bool named_upgradable_mutex::timed_unlock_upgradable_and_lock + (const boost::posix_time::ptime &abs_time) +{ return this->mutex()->timed_unlock_upgradable_and_lock(abs_time); } + +inline bool named_upgradable_mutex::try_unlock_sharable_and_lock() +{ return this->mutex()->try_unlock_sharable_and_lock(); } + +inline bool named_upgradable_mutex::try_unlock_sharable_and_lock_upgradable() +{ return this->mutex()->try_unlock_sharable_and_lock_upgradable(); } + +inline bool named_upgradable_mutex::remove(const char *name) +{ return shared_memory_object::remove(name); } + +/// @endcond + +} //namespace interprocess { +} //namespace boost { + +#include <boost/interprocess/detail/config_end.hpp> + +#endif //BOOST_INTERPROCESS_NAMED_UPGRADABLE_MUTEX_HPP diff --git a/boost/interprocess/sync/spin/condition.hpp b/boost/interprocess/sync/spin/condition.hpp new file mode 100644 index 0000000000..5a37d9be2d --- /dev/null +++ b/boost/interprocess/sync/spin/condition.hpp @@ -0,0 +1,295 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2005-2011. 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_DETAIL_SPIN_CONDITION_HPP +#define BOOST_INTERPROCESS_DETAIL_SPIN_CONDITION_HPP + +#include <boost/interprocess/detail/config_begin.hpp> +#include <boost/interprocess/detail/workaround.hpp> +#include <boost/interprocess/sync/spin/mutex.hpp> +#include <boost/interprocess/detail/posix_time_types_wrk.hpp> +#include <boost/interprocess/detail/atomic.hpp> +#include <boost/interprocess/sync/scoped_lock.hpp> +#include <boost/interprocess/exceptions.hpp> +#include <boost/interprocess/detail/os_thread_functions.hpp> +#include <boost/move/move.hpp> +#include <boost/cstdint.hpp> + +namespace boost { +namespace interprocess { +namespace ipcdetail { + +class spin_condition +{ + spin_condition(const spin_condition &); + spin_condition &operator=(const spin_condition &); + public: + spin_condition(); + ~spin_condition(); + + void notify_one(); + void notify_all(); + + template <typename L> + bool timed_wait(L& lock, const boost::posix_time::ptime &abs_time) + { + if(abs_time == boost::posix_time::pos_infin){ + this->wait(lock); + return true; + } + if (!lock) + throw lock_exception(); + return this->do_timed_wait(abs_time, *lock.mutex()); + } + + template <typename L, typename Pr> + bool timed_wait(L& lock, const boost::posix_time::ptime &abs_time, Pr pred) + { + if(abs_time == boost::posix_time::pos_infin){ + this->wait(lock, pred); + return true; + } + if (!lock) + throw lock_exception(); + while (!pred()){ + if (!this->do_timed_wait(abs_time, *lock.mutex())) + return pred(); + } + return true; + } + + template <typename L> + void wait(L& lock) + { + if (!lock) + throw lock_exception(); + do_wait(*lock.mutex()); + } + + template <typename L, typename Pr> + void wait(L& lock, Pr pred) + { + if (!lock) + throw lock_exception(); + + while (!pred()) + do_wait(*lock.mutex()); + } + + template<class InterprocessMutex> + void do_wait(InterprocessMutex &mut); + + template<class InterprocessMutex> + bool do_timed_wait(const boost::posix_time::ptime &abs_time, InterprocessMutex &mut); + + private: + template<class InterprocessMutex> + bool do_timed_wait(bool tout_enabled, const boost::posix_time::ptime &abs_time, InterprocessMutex &mut); + + enum { SLEEP = 0, NOTIFY_ONE, NOTIFY_ALL }; + spin_mutex m_enter_mut; + volatile boost::uint32_t m_command; + volatile boost::uint32_t m_num_waiters; + void notify(boost::uint32_t command); +}; + +inline spin_condition::spin_condition() +{ + //Note that this class is initialized to zero. + //So zeroed memory can be interpreted as an initialized + //condition variable + m_command = SLEEP; + m_num_waiters = 0; +} + +inline spin_condition::~spin_condition() +{ + //Trivial destructor +} + +inline void spin_condition::notify_one() +{ + this->notify(NOTIFY_ONE); +} + +inline void spin_condition::notify_all() +{ + this->notify(NOTIFY_ALL); +} + +inline void spin_condition::notify(boost::uint32_t command) +{ + //This mutex guarantees that no other thread can enter to the + //do_timed_wait method logic, so that thread count will be + //constant until the function writes a NOTIFY_ALL command. + //It also guarantees that no other notification can be signaled + //on this spin_condition before this one ends + m_enter_mut.lock(); + + //Return if there are no waiters + if(!atomic_read32(&m_num_waiters)) { + m_enter_mut.unlock(); + return; + } + + //Notify that all threads should execute wait logic + while(SLEEP != atomic_cas32(const_cast<boost::uint32_t*>(&m_command), command, SLEEP)){ + thread_yield(); + } +/* + //Wait until the threads are woken + while(SLEEP != atomic_cas32(const_cast<boost::uint32_t*>(&m_command), 0)){ + thread_yield(); + } +*/ + //The enter mutex will rest locked until the last waiting thread unlocks it +} + +template<class InterprocessMutex> +inline void spin_condition::do_wait(InterprocessMutex &mut) +{ + this->do_timed_wait(false, boost::posix_time::ptime(), mut); +} + +template<class InterprocessMutex> +inline bool spin_condition::do_timed_wait + (const boost::posix_time::ptime &abs_time, InterprocessMutex &mut) +{ + return this->do_timed_wait(true, abs_time, mut); +} + +template<class InterprocessMutex> +inline bool spin_condition::do_timed_wait(bool tout_enabled, + const boost::posix_time::ptime &abs_time, + InterprocessMutex &mut) +{ + boost::posix_time::ptime now = microsec_clock::universal_time(); + + if(tout_enabled){ + if(now >= abs_time) return false; + } + + typedef boost::interprocess::scoped_lock<spin_mutex> InternalLock; + //The enter mutex guarantees that while executing a notification, + //no other thread can execute the do_timed_wait method. + { + //--------------------------------------------------------------- + InternalLock lock; + if(tout_enabled){ + InternalLock dummy(m_enter_mut, abs_time); + lock = boost::move(dummy); + } + else{ + InternalLock dummy(m_enter_mut); + lock = boost::move(dummy); + } + + if(!lock) + return false; + //--------------------------------------------------------------- + //We increment the waiting thread count protected so that it will be + //always constant when another thread enters the notification logic. + //The increment marks this thread as "waiting on spin_condition" + atomic_inc32(const_cast<boost::uint32_t*>(&m_num_waiters)); + + //We unlock the external mutex atomically with the increment + mut.unlock(); + } + + //By default, we suppose that no timeout has happened + bool timed_out = false, unlock_enter_mut= false; + + //Loop until a notification indicates that the thread should + //exit or timeout occurs + while(1){ + //The thread sleeps/spins until a spin_condition commands a notification + //Notification occurred, we will lock the checking mutex so that + while(atomic_read32(&m_command) == SLEEP){ + thread_yield(); + + //Check for timeout + if(tout_enabled){ + now = microsec_clock::universal_time(); + + if(now >= abs_time){ + //If we can lock the mutex it means that no notification + //is being executed in this spin_condition variable + timed_out = m_enter_mut.try_lock(); + + //If locking fails, indicates that another thread is executing + //notification, so we play the notification game + if(!timed_out){ + //There is an ongoing notification, we will try again later + continue; + } + //No notification in execution, since enter mutex is locked. + //We will execute time-out logic, so we will decrement count, + //release the enter mutex and return false. + break; + } + } + } + + //If a timeout occurred, the mutex will not execute checking logic + if(tout_enabled && timed_out){ + //Decrement wait count + atomic_dec32(const_cast<boost::uint32_t*>(&m_num_waiters)); + unlock_enter_mut = true; + break; + } + else{ + boost::uint32_t result = atomic_cas32 + (const_cast<boost::uint32_t*>(&m_command), SLEEP, NOTIFY_ONE); + if(result == SLEEP){ + //Other thread has been notified and since it was a NOTIFY one + //command, this thread must sleep again + continue; + } + else if(result == NOTIFY_ONE){ + //If it was a NOTIFY_ONE command, only this thread should + //exit. This thread has atomically marked command as sleep before + //so no other thread will exit. + //Decrement wait count. + unlock_enter_mut = true; + atomic_dec32(const_cast<boost::uint32_t*>(&m_num_waiters)); + break; + } + else{ + //If it is a NOTIFY_ALL command, all threads should return + //from do_timed_wait function. Decrement wait count. + unlock_enter_mut = 1 == atomic_dec32(const_cast<boost::uint32_t*>(&m_num_waiters)); + //Check if this is the last thread of notify_all waiters + //Only the last thread will release the mutex + if(unlock_enter_mut){ + atomic_cas32(const_cast<boost::uint32_t*>(&m_command), SLEEP, NOTIFY_ALL); + } + break; + } + } + } + + //Unlock the enter mutex if it is a single notification, if this is + //the last notified thread in a notify_all or a timeout has occurred + if(unlock_enter_mut){ + m_enter_mut.unlock(); + } + + //Lock external again before returning from the method + mut.lock(); + return !timed_out; +} + +} //namespace ipcdetail +} //namespace interprocess +} //namespace boost + +#include <boost/interprocess/detail/config_end.hpp> + +#endif //BOOST_INTERPROCESS_DETAIL_SPIN_CONDITION_HPP diff --git a/boost/interprocess/sync/spin/interprocess_barrier.hpp b/boost/interprocess/sync/spin/interprocess_barrier.hpp new file mode 100644 index 0000000000..f8ee09948d --- /dev/null +++ b/boost/interprocess/sync/spin/interprocess_barrier.hpp @@ -0,0 +1,46 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2006. 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. +// +////////////////////////////////////////////////////////////////////////////// + +#include<boost/interprocess/exceptions.hpp> +#include <boost/interprocess/detail/posix_time_types_wrk.hpp> +#include <boost/interprocess/sync/scoped_lock.hpp> + +namespace boost { +namespace interprocess { + +inline barrier::barrier(unsigned int count) + : m_threshold(count), m_count(count), m_generation(0) +{ + if (count == 0) + throw std::invalid_argument("count cannot be zero."); +} + +inline barrier::~barrier(){} + +inline bool barrier::wait() +{ + scoped_lock<interprocess_mutex> lock(m_mutex); + unsigned int gen = m_generation; + + if (--m_count == 0){ + m_generation++; + m_count = m_threshold; + m_cond.notify_all(); + return true; + } + + while (gen == m_generation){ + m_cond.wait(lock); + } + return false; +} + +} //namespace interprocess { +} //namespace boost { diff --git a/boost/interprocess/sync/spin/mutex.hpp b/boost/interprocess/sync/spin/mutex.hpp new file mode 100644 index 0000000000..ef0b47d95a --- /dev/null +++ b/boost/interprocess/sync/spin/mutex.hpp @@ -0,0 +1,114 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2005-2011. 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_DETAIL_SPIN_MUTEX_HPP +#define BOOST_INTERPROCESS_DETAIL_SPIN_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/detail/posix_time_types_wrk.hpp> +#include <boost/assert.hpp> +#include <boost/interprocess/detail/atomic.hpp> +#include <boost/cstdint.hpp> +#include <boost/interprocess/detail/os_thread_functions.hpp> + +namespace boost { +namespace interprocess { +namespace ipcdetail { + +class spin_mutex +{ + spin_mutex(const spin_mutex &); + spin_mutex &operator=(const spin_mutex &); + public: + + spin_mutex(); + ~spin_mutex(); + + void lock(); + bool try_lock(); + bool timed_lock(const boost::posix_time::ptime &abs_time); + void unlock(); + void take_ownership(){}; + private: + volatile boost::uint32_t m_s; +}; + +inline spin_mutex::spin_mutex() + : m_s(0) +{ + //Note that this class is initialized to zero. + //So zeroed memory can be interpreted as an + //initialized mutex +} + +inline spin_mutex::~spin_mutex() +{ + //Trivial destructor +} + +inline void spin_mutex::lock(void) +{ + do{ + boost::uint32_t prev_s = ipcdetail::atomic_cas32(const_cast<boost::uint32_t*>(&m_s), 1, 0); + + if (m_s == 1 && prev_s == 0){ + break; + } + // relinquish current timeslice + ipcdetail::thread_yield(); + }while (true); +} + +inline bool spin_mutex::try_lock(void) +{ + boost::uint32_t prev_s = ipcdetail::atomic_cas32(const_cast<boost::uint32_t*>(&m_s), 1, 0); + return m_s == 1 && prev_s == 0; +} + +inline bool spin_mutex::timed_lock(const boost::posix_time::ptime &abs_time) +{ + if(abs_time == boost::posix_time::pos_infin){ + this->lock(); + return true; + } + //Obtain current count and target time + boost::posix_time::ptime now = microsec_clock::universal_time(); + + do{ + if(this->try_lock()){ + break; + } + now = microsec_clock::universal_time(); + + if(now >= abs_time){ + return false; + } + // relinquish current time slice + ipcdetail::thread_yield(); + }while (true); + + return true; +} + +inline void spin_mutex::unlock(void) +{ ipcdetail::atomic_cas32(const_cast<boost::uint32_t*>(&m_s), 0, 1); } + +} //namespace ipcdetail { +} //namespace interprocess { +} //namespace boost { + +#include <boost/interprocess/detail/config_end.hpp> + +#endif //BOOST_INTERPROCESS_DETAIL_SPIN_MUTEX_HPP diff --git a/boost/interprocess/sync/spin/recursive_mutex.hpp b/boost/interprocess/sync/spin/recursive_mutex.hpp new file mode 100644 index 0000000000..05ad65eac0 --- /dev/null +++ b/boost/interprocess/sync/spin/recursive_mutex.hpp @@ -0,0 +1,175 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2005-2011. 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. +// +////////////////////////////////////////////////////////////////////////////// +// +// Parts of the pthread code come from Boost Threads code: +// +////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2001-2003 +// William E. Kempf +// +// Permission to use, copy, modify, distribute and sell this software +// and its documentation for any purpose is hereby granted without fee, +// provided that the above copyright notice appear in all copies and +// that both that copyright notice and this permission notice appear +// in supporting documentation. William E. Kempf makes no representations +// about the suitability of this software for any purpose. +// It is provided "as is" without express or implied warranty. +////////////////////////////////////////////////////////////////////////////// + +#ifndef BOOST_INTERPROCESS_DETAIL_SPIN_RECURSIVE_MUTEX_HPP +#define BOOST_INTERPROCESS_DETAIL_SPIN_RECURSIVE_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/detail/posix_time_types_wrk.hpp> +#include <boost/interprocess/detail/os_thread_functions.hpp> +#include <boost/interprocess/exceptions.hpp> +#include <boost/interprocess/detail/atomic.hpp> +#include <boost/cstdint.hpp> +#include <boost/interprocess/detail/os_thread_functions.hpp> +#include <boost/interprocess/sync/spin/mutex.hpp> +#include <boost/assert.hpp> + +namespace boost { +namespace interprocess { +namespace ipcdetail { + +class spin_recursive_mutex +{ + spin_recursive_mutex(const spin_recursive_mutex &); + spin_recursive_mutex &operator=(const spin_recursive_mutex &); + public: + + spin_recursive_mutex(); + ~spin_recursive_mutex(); + + void lock(); + bool try_lock(); + bool timed_lock(const boost::posix_time::ptime &abs_time); + void unlock(); + void take_ownership(); + private: + spin_mutex m_mutex; + unsigned int m_nLockCount; + volatile ipcdetail::OS_systemwide_thread_id_t m_nOwner; + volatile boost::uint32_t m_s; +}; + +inline spin_recursive_mutex::spin_recursive_mutex() + : m_nLockCount(0), m_nOwner(ipcdetail::get_invalid_systemwide_thread_id()){} + +inline spin_recursive_mutex::~spin_recursive_mutex(){} + +inline void spin_recursive_mutex::lock() +{ + typedef ipcdetail::OS_systemwide_thread_id_t handle_t; + const handle_t thr_id(ipcdetail::get_current_systemwide_thread_id()); + handle_t old_id; + ipcdetail::systemwide_thread_id_copy(m_nOwner, old_id); + if(ipcdetail::equal_systemwide_thread_id(thr_id , old_id)){ + if((unsigned int)(m_nLockCount+1) == 0){ + //Overflow, throw an exception + throw interprocess_exception("boost::interprocess::spin_recursive_mutex recursive lock overflow"); + } + ++m_nLockCount; + } + else{ + m_mutex.lock(); + ipcdetail::systemwide_thread_id_copy(thr_id, m_nOwner); + m_nLockCount = 1; + } +} + +inline bool spin_recursive_mutex::try_lock() +{ + typedef ipcdetail::OS_systemwide_thread_id_t handle_t; + handle_t thr_id(ipcdetail::get_current_systemwide_thread_id()); + handle_t old_id; + ipcdetail::systemwide_thread_id_copy(m_nOwner, old_id); + if(ipcdetail::equal_systemwide_thread_id(thr_id , old_id)) { // we own it + if((unsigned int)(m_nLockCount+1) == 0){ + //Overflow, throw an exception + throw interprocess_exception("boost::interprocess::spin_recursive_mutex recursive lock overflow"); + } + ++m_nLockCount; + return true; + } + if(m_mutex.try_lock()){ + ipcdetail::systemwide_thread_id_copy(thr_id, m_nOwner); + m_nLockCount = 1; + return true; + } + return false; +} + +inline bool spin_recursive_mutex::timed_lock(const boost::posix_time::ptime &abs_time) +{ + typedef ipcdetail::OS_systemwide_thread_id_t handle_t; + if(abs_time == boost::posix_time::pos_infin){ + this->lock(); + return true; + } + const handle_t thr_id(ipcdetail::get_current_systemwide_thread_id()); + handle_t old_id; + ipcdetail::systemwide_thread_id_copy(m_nOwner, old_id); + if(ipcdetail::equal_systemwide_thread_id(thr_id , old_id)) { // we own it + if((unsigned int)(m_nLockCount+1) == 0){ + //Overflow, throw an exception + throw interprocess_exception("boost::interprocess::spin_recursive_mutex recursive lock overflow"); + } + ++m_nLockCount; + return true; + } + if(m_mutex.timed_lock(abs_time)){ + ipcdetail::systemwide_thread_id_copy(thr_id, m_nOwner); + m_nLockCount = 1; + return true; + } + return false; +} + +inline void spin_recursive_mutex::unlock() +{ + typedef ipcdetail::OS_systemwide_thread_id_t handle_t; + handle_t old_id; + ipcdetail::systemwide_thread_id_copy(m_nOwner, old_id); + const handle_t thr_id(ipcdetail::get_current_systemwide_thread_id()); + (void)old_id; + (void)thr_id; + BOOST_ASSERT(ipcdetail::equal_systemwide_thread_id(thr_id, old_id)); + --m_nLockCount; + if(!m_nLockCount){ + const handle_t new_id(ipcdetail::get_invalid_systemwide_thread_id()); + ipcdetail::systemwide_thread_id_copy(new_id, m_nOwner); + m_mutex.unlock(); + } +} + +inline void spin_recursive_mutex::take_ownership() +{ + typedef ipcdetail::OS_systemwide_thread_id_t handle_t; + this->m_nLockCount = 1; + const handle_t thr_id(ipcdetail::get_current_systemwide_thread_id()); + ipcdetail::systemwide_thread_id_copy(thr_id, m_nOwner); +} + +} //namespace ipcdetail { +} //namespace interprocess { +} //namespace boost { + +#include <boost/interprocess/detail/config_end.hpp> + +#endif //BOOST_INTERPROCESS_DETAIL_SPIN_RECURSIVE_MUTEX_HPP diff --git a/boost/interprocess/sync/spin/semaphore.hpp b/boost/interprocess/sync/spin/semaphore.hpp new file mode 100644 index 0000000000..1b8cac35aa --- /dev/null +++ b/boost/interprocess/sync/spin/semaphore.hpp @@ -0,0 +1,110 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2005-2011. 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_DETAIL_SPIN_SEMAPHORE_HPP +#define BOOST_INTERPROCESS_DETAIL_SPIN_SEMAPHORE_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/detail/atomic.hpp> +#include <boost/interprocess/detail/os_thread_functions.hpp> +#include <boost/interprocess/detail/posix_time_types_wrk.hpp> +#include <boost/cstdint.hpp> + +namespace boost { +namespace interprocess { +namespace ipcdetail { + +class spin_semaphore +{ + spin_semaphore(const spin_semaphore &); + spin_semaphore &operator=(const spin_semaphore &); + + public: + spin_semaphore(unsigned int initialCount); + ~spin_semaphore(); + + void post(); + void wait(); + bool try_wait(); + bool timed_wait(const boost::posix_time::ptime &abs_time); + +// int get_count() const; + private: + volatile boost::uint32_t m_count; +}; + + +inline spin_semaphore::~spin_semaphore() +{} + +inline spin_semaphore::spin_semaphore(unsigned int initialCount) +{ ipcdetail::atomic_write32(&this->m_count, boost::uint32_t(initialCount)); } + +inline void spin_semaphore::post() +{ + ipcdetail::atomic_inc32(&m_count); +} + +inline void spin_semaphore::wait() +{ + while(!ipcdetail::atomic_add_unless32(&m_count, boost::uint32_t(-1), boost::uint32_t(0))){ + while(ipcdetail::atomic_read32(&m_count) == 0){ + ipcdetail::thread_yield(); + } + } +} + +inline bool spin_semaphore::try_wait() +{ + return ipcdetail::atomic_add_unless32(&m_count, boost::uint32_t(-1), boost::uint32_t(0)); +} + +inline bool spin_semaphore::timed_wait(const boost::posix_time::ptime &abs_time) +{ + if(abs_time == boost::posix_time::pos_infin){ + this->wait(); + return true; + } + //Obtain current count and target time + boost::posix_time::ptime now(microsec_clock::universal_time()); + + do{ + if(this->try_wait()){ + break; + } + now = microsec_clock::universal_time(); + + if(now >= abs_time){ + return this->try_wait(); + } + // relinquish current time slice + ipcdetail::thread_yield(); + }while (true); + return true; +} + + +//inline int spin_semaphore::get_count() const +//{ + //return (int)ipcdetail::atomic_read32(&m_count); +//} + +} //namespace ipcdetail { +} //namespace interprocess { +} //namespace boost { + +#include <boost/interprocess/detail/config_end.hpp> + +#endif //BOOST_INTERPROCESS_DETAIL_SPIN_SEMAPHORE_HPP diff --git a/boost/interprocess/sync/upgradable_lock.hpp b/boost/interprocess/sync/upgradable_lock.hpp new file mode 100644 index 0000000000..93c2ed6bc4 --- /dev/null +++ b/boost/interprocess/sync/upgradable_lock.hpp @@ -0,0 +1,309 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2005-2011. 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. +// +////////////////////////////////////////////////////////////////////////////// +// +// This interface is inspired by Howard Hinnant's lock proposal. +// http://home.twcny.rr.com/hinnant/cpp_extensions/threads_move.html +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef BOOST_INTERPROCESS_UPGRADABLE_LOCK_HPP +#define BOOST_INTERPROCESS_UPGRADABLE_LOCK_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/interprocess_fwd.hpp> +#include <boost/interprocess/sync/lock_options.hpp> +#include <boost/interprocess/detail/mpl.hpp> +#include <boost/interprocess/detail/type_traits.hpp> + +#include <boost/interprocess/exceptions.hpp> +#include <boost/move/move.hpp> +#include <boost/interprocess/detail/posix_time_types_wrk.hpp> + +//!\file +//!Describes the upgradable_lock class that serves to acquire the upgradable +//!lock of a mutex. + +namespace boost { +namespace interprocess { + +//!upgradable_lock is meant to carry out the tasks for read-locking, unlocking, +//!try-read-locking and timed-read-locking (recursive or not) for the Mutex. +//!Additionally the upgradable_lock can transfer ownership to a scoped_lock +//!using transfer_lock syntax. The Mutex need not supply all of the functionality. +//!If the client of upgradable_lock<Mutex> does not use functionality which the +//!Mutex does not supply, no harm is done. Mutex ownership can be shared among +//!read_locks, and a single upgradable_lock. upgradable_lock does not support +//!copy semantics. However upgradable_lock supports ownership transfer from +//!a upgradable_locks or scoped_locks via transfer_lock syntax. +template <class UpgradableMutex> +class upgradable_lock +{ + public: + typedef UpgradableMutex mutex_type; + /// @cond + private: + typedef upgradable_lock<UpgradableMutex> this_type; + explicit upgradable_lock(scoped_lock<mutex_type>&); + typedef bool this_type::*unspecified_bool_type; + BOOST_MOVABLE_BUT_NOT_COPYABLE(upgradable_lock) + /// @endcond + public: + + //!Effects: Default constructs a upgradable_lock. + //!Postconditions: owns() == false and mutex() == 0. + upgradable_lock() + : mp_mutex(0), m_locked(false) + {} + + explicit upgradable_lock(mutex_type& m) + : mp_mutex(&m), m_locked(false) + { mp_mutex->lock_upgradable(); m_locked = true; } + + //!Postconditions: owns() == false, and mutex() == &m. + //!Notes: The constructor will not take ownership of the mutex. There is no effect + //! required on the referenced mutex. + upgradable_lock(mutex_type& m, defer_lock_type) + : mp_mutex(&m), m_locked(false) + {} + + //!Postconditions: owns() == true, and mutex() == &m. + //!Notes: The constructor will suppose that the mutex is already upgradable + //! locked. There is no effect required on the referenced mutex. + upgradable_lock(mutex_type& m, accept_ownership_type) + : mp_mutex(&m), m_locked(true) + {} + + //!Effects: m.try_lock_upgradable(). + //!Postconditions: mutex() == &m. owns() == the return value of the + //! m.try_lock_upgradable() executed within the constructor. + //!Notes: The constructor will take upgradable-ownership of the mutex + //! if it can do so without waiting. Whether or not this constructor + //! handles recursive locking depends upon the mutex. If the mutex_type + //! does not support try_lock_upgradable, this constructor will fail at + //! compile time if instantiated, but otherwise have no effect. + upgradable_lock(mutex_type& m, try_to_lock_type) + : mp_mutex(&m), m_locked(false) + { m_locked = mp_mutex->try_lock_upgradable(); } + + //!Effects: m.timed_lock_upgradable(abs_time) + //!Postconditions: mutex() == &m. owns() == the return value of the + //! m.timed_lock_upgradable() executed within the constructor. + //!Notes: The constructor will take upgradable-ownership of the mutex if it + //! can do so within the time specified. Whether or not this constructor + //! handles recursive locking depends upon the mutex. If the mutex_type + //! does not support timed_lock_upgradable, this constructor will fail + //! at compile time if instantiated, but otherwise have no effect. + upgradable_lock(mutex_type& m, const boost::posix_time::ptime& abs_time) + : mp_mutex(&m), m_locked(false) + { m_locked = mp_mutex->timed_lock_upgradable(abs_time); } + + //!Effects: No effects on the underlying mutex. + //!Postconditions: mutex() == the value upgr.mutex() had before the + //! construction. upgr.mutex() == 0. owns() == upgr.owns() before the + //! construction. upgr.owns() == false. + //!Notes: If upgr is locked, this constructor will lock this upgradable_lock + //! while unlocking upgr. If upgr is unlocked, then this upgradable_lock will + //! be unlocked as well. Only a moved upgradable_lock's will match this + //! signature. An non-moved upgradable_lock can be moved with the + //! expression: "boost::move(lock);". This constructor does not alter the + //! state of the mutex, only potentially who owns it. + upgradable_lock(BOOST_RV_REF(upgradable_lock<mutex_type>) upgr) + : mp_mutex(0), m_locked(upgr.owns()) + { mp_mutex = upgr.release(); } + + //!Effects: If scop.owns(), m_.unlock_and_lock_upgradable(). + //!Postconditions: mutex() == the value scop.mutex() had before the construction. + //! scop.mutex() == 0. owns() == scop.owns() before the constructor. After the + //! construction, scop.owns() == false. + //!Notes: If scop is locked, this constructor will transfer the exclusive-ownership + //! to an upgradable-ownership of this upgradable_lock. + //! Only a moved sharable_lock's will match this + //! signature. An non-moved sharable_lock can be moved with the + //! expression: "boost::move(lock);". + template<class T> + upgradable_lock(BOOST_RV_REF(scoped_lock<T>) scop + , typename ipcdetail::enable_if< ipcdetail::is_same<T, UpgradableMutex> >::type * = 0) + : mp_mutex(0), m_locked(false) + { + scoped_lock<mutex_type> &u_lock = scop; + if(u_lock.owns()){ + u_lock.mutex()->unlock_and_lock_upgradable(); + m_locked = true; + } + mp_mutex = u_lock.release(); + } + + //!Effects: If shar.owns() then calls try_unlock_sharable_and_lock_upgradable() + //! on the referenced mutex. + //! a)if try_unlock_sharable_and_lock_upgradable() returns true then mutex() + //! obtains the value from shar.release() and owns() is set to true. + //! b)if try_unlock_sharable_and_lock_upgradable() returns false then shar is + //! unaffected and this upgradable_lock construction has the same + //! effects as a default construction. + //! c)Else shar.owns() is false. mutex() obtains the value from shar.release() + //! and owns() is set to false. + //!Notes: This construction will not block. It will try to obtain mutex + //! ownership from shar immediately, while changing the lock type from a + //! "read lock" to an "upgradable lock". If the "read lock" isn't held + //! in the first place, the mutex merely changes type to an unlocked + //! "upgradable lock". If the "read lock" is held, then mutex transfer + //! occurs only if it can do so in a non-blocking manner. + template<class T> + upgradable_lock( BOOST_RV_REF(sharable_lock<T>) shar, try_to_lock_type + , typename ipcdetail::enable_if< ipcdetail::is_same<T, UpgradableMutex> >::type * = 0) + : mp_mutex(0), m_locked(false) + { + sharable_lock<mutex_type> &s_lock = shar; + if(s_lock.owns()){ + if((m_locked = s_lock.mutex()->try_unlock_sharable_and_lock_upgradable()) == true){ + mp_mutex = s_lock.release(); + } + } + else{ + s_lock.release(); + } + } + + //!Effects: if (owns()) m_->unlock_upgradable(). + //!Notes: The destructor behavior ensures that the mutex lock is not leaked. + ~upgradable_lock() + { + try{ + if(m_locked && mp_mutex) mp_mutex->unlock_upgradable(); + } + catch(...){} + } + + //!Effects: If owns(), then unlock_upgradable() is called on mutex(). + //! *this gets the state of upgr and upgr gets set to a default constructed state. + //!Notes: With a recursive mutex it is possible that both this and upgr own the + //! mutex before the assignment. In this case, this will own the mutex + //! after the assignment (and upgr will not), but the mutex's upgradable lock + //! count will be decremented by one. + upgradable_lock &operator=(BOOST_RV_REF(upgradable_lock) upgr) + { + if(this->owns()) + this->unlock(); + m_locked = upgr.owns(); + mp_mutex = upgr.release(); + return *this; + } + + //!Effects: If mutex() == 0 or if already locked, throws a lock_exception() + //! exception. Calls lock_upgradable() on the referenced mutex. + //!Postconditions: owns() == true. + //!Notes: The sharable_lock changes from a state of not owning the mutex, + //! to owning the mutex, blocking if necessary. + void lock() + { + if(!mp_mutex || m_locked) + throw lock_exception(); + mp_mutex->lock_upgradable(); + m_locked = true; + } + + //!Effects: If mutex() == 0 or if already locked, throws a lock_exception() + //! exception. Calls try_lock_upgradable() on the referenced mutex. + //!Postconditions: owns() == the value returned from + //! mutex()->try_lock_upgradable(). + //!Notes: The upgradable_lock changes from a state of not owning the mutex, + //! to owning the mutex, but only if blocking was not required. If the + //! mutex_type does not support try_lock_upgradable(), this function will + //! fail at compile time if instantiated, but otherwise have no effect. + bool try_lock() + { + if(!mp_mutex || m_locked) + throw lock_exception(); + m_locked = mp_mutex->try_lock_upgradable(); + return m_locked; + } + + //!Effects: If mutex() == 0 or if already locked, throws a lock_exception() + //! exception. Calls timed_lock_upgradable(abs_time) on the referenced mutex. + //!Postconditions: owns() == the value returned from + //! mutex()->timed_lock_upgradable(abs_time). + //!Notes: The upgradable_lock changes from a state of not owning the mutex, + //! to owning the mutex, but only if it can obtain ownership within the + //! specified time. If the mutex_type does not support + //! timed_lock_upgradable(abs_time), this function will fail at compile + //! time if instantiated, but otherwise have no effect. + bool timed_lock(const boost::posix_time::ptime& abs_time) + { + if(!mp_mutex || m_locked) + throw lock_exception(); + m_locked = mp_mutex->timed_lock_upgradable(abs_time); + return m_locked; + } + + //!Effects: If mutex() == 0 or if not locked, throws a lock_exception() + //! exception. Calls unlock_upgradable() on the referenced mutex. + //!Postconditions: owns() == false. + //!Notes: The upgradable_lock changes from a state of owning the mutex, + //! to not owning the mutex. + void unlock() + { + if(!mp_mutex || !m_locked) + throw lock_exception(); + mp_mutex->unlock_upgradable(); + m_locked = false; + } + + //!Effects: Returns true if this scoped_lock has acquired the + //!referenced mutex. + bool owns() const + { return m_locked && mp_mutex; } + + //!Conversion to bool. + //!Returns owns(). + operator unspecified_bool_type() const + { return m_locked? &this_type::m_locked : 0; } + + //!Effects: Returns a pointer to the referenced mutex, or 0 if + //!there is no mutex to reference. + mutex_type* mutex() const + { return mp_mutex; } + + //!Effects: Returns a pointer to the referenced mutex, or 0 if there is no + //! mutex to reference. + //!Postconditions: mutex() == 0 and owns() == false. + mutex_type* release() + { + mutex_type *mut = mp_mutex; + mp_mutex = 0; + m_locked = false; + return mut; + } + + //!Effects: Swaps state with moved lock. + //!Throws: Nothing. + void swap(upgradable_lock<mutex_type> &other) + { + std::swap(mp_mutex, other.mp_mutex); + std::swap(m_locked, other.m_locked); + } + + /// @cond + private: + mutex_type *mp_mutex; + bool m_locked; + /// @endcond +}; + +} // namespace interprocess +} // namespace boost + +#include <boost/interprocess/detail/config_end.hpp> + +#endif // BOOST_INTERPROCESS_UPGRADABLE_LOCK_HPP diff --git a/boost/interprocess/sync/windows/condition.hpp b/boost/interprocess/sync/windows/condition.hpp new file mode 100644 index 0000000000..167b8730c3 --- /dev/null +++ b/boost/interprocess/sync/windows/condition.hpp @@ -0,0 +1,383 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2005-2011. 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_DETAIL_WINDOWS_CONDITION_HPP +#define BOOST_INTERPROCESS_DETAIL_WINDOWS_CONDITION_HPP + +#include <boost/interprocess/detail/config_begin.hpp> +#include <boost/interprocess/detail/workaround.hpp> +#include <boost/interprocess/detail/posix_time_types_wrk.hpp> + +#include <boost/interprocess/sync/interprocess_mutex.hpp> +#include <boost/interprocess/sync/scoped_lock.hpp> +#include <boost/interprocess/exceptions.hpp> +#include <boost/interprocess/sync/windows/semaphore.hpp> +#include <boost/interprocess/sync/windows/mutex.hpp> +#include <boost/cstdint.hpp> +#include <limits> + +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +// +// Condition variable algorithm taken from pthreads-win32 discussion. +// +// The algorithm was developed by Alexander Terekhov in colaboration with +// Louis Thomas. +// +// Algorithm 8a / IMPL_SEM,UNBLOCK_STRATEGY == UNBLOCK_ALL +// +// semBlockLock - bin.semaphore +// semBlockQueue - semaphore +// mtxExternal - mutex or CS +// mtxUnblockLock - mutex or CS +// nWaitersGone - int +// nWaitersBlocked - int +// nWaitersToUnblock - int +// +// wait( timeout ) { +// +// [auto: register int result ] // error checking omitted +// [auto: register int nSignalsWasLeft ] +// [auto: register int nWaitersWasGone ] +// +// sem_wait( semBlockLock ); +// nWaitersBlocked++; +// sem_post( semBlockLock ); +// +// unlock( mtxExternal ); +// bTimedOut = sem_wait( semBlockQueue,timeout ); +// +// lock( mtxUnblockLock ); +// if ( 0 != (nSignalsWasLeft = nWaitersToUnblock) ) { +// if ( bTimedOut ) { // timeout (or canceled) +// if ( 0 != nWaitersBlocked ) { +// nWaitersBlocked--; +// } +// else { +// nWaitersGone++; // count spurious wakeups. +// } +// } +// if ( 0 == --nWaitersToUnblock ) { +// if ( 0 != nWaitersBlocked ) { +// sem_post( semBlockLock ); // open the gate. +// nSignalsWasLeft = 0; // do not open the gate +// // below again. +// } +// else if ( 0 != (nWaitersWasGone = nWaitersGone) ) { +// nWaitersGone = 0; +// } +// } +// } +// else if ( INT_MAX/2 == ++nWaitersGone ) { // timeout/canceled or +// // spurious semaphore :-) +// sem_wait( semBlockLock ); +// nWaitersBlocked -= nWaitersGone; // something is going on here +// // - test of timeouts? :-) +// sem_post( semBlockLock ); +// nWaitersGone = 0; +// } +// unlock( mtxUnblockLock ); +// +// if ( 1 == nSignalsWasLeft ) { +// if ( 0 != nWaitersWasGone ) { +// // sem_adjust( semBlockQueue,-nWaitersWasGone ); +// while ( nWaitersWasGone-- ) { +// sem_wait( semBlockQueue ); // better now than spurious later +// } +// } sem_post( semBlockLock ); // open the gate +// } +// +// lock( mtxExternal ); +// +// return ( bTimedOut ) ? ETIMEOUT : 0; +// } +// +// signal(bAll) { +// +// [auto: register int result ] +// [auto: register int nSignalsToIssue] +// +// lock( mtxUnblockLock ); +// +// if ( 0 != nWaitersToUnblock ) { // the gate is closed!!! +// if ( 0 == nWaitersBlocked ) { // NO-OP +// return unlock( mtxUnblockLock ); +// } +// if (bAll) { +// nWaitersToUnblock += nSignalsToIssue=nWaitersBlocked; +// nWaitersBlocked = 0; +// } +// else { +// nSignalsToIssue = 1; +// nWaitersToUnblock++; +// nWaitersBlocked--; +// } +// } +// else if ( nWaitersBlocked > nWaitersGone ) { // HARMLESS RACE CONDITION! +// sem_wait( semBlockLock ); // close the gate +// if ( 0 != nWaitersGone ) { +// nWaitersBlocked -= nWaitersGone; +// nWaitersGone = 0; +// } +// if (bAll) { +// nSignalsToIssue = nWaitersToUnblock = nWaitersBlocked; +// nWaitersBlocked = 0; +// } +// else { +// nSignalsToIssue = nWaitersToUnblock = 1; +// nWaitersBlocked--; +// } +// } +// else { // NO-OP +// return unlock( mtxUnblockLock ); +// } +// +// unlock( mtxUnblockLock ); +// sem_post( semBlockQueue,nSignalsToIssue ); +// return result; +// } +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// + +namespace boost { +namespace interprocess { +namespace ipcdetail { + +class windows_condition +{ + windows_condition(const windows_condition &); + windows_condition &operator=(const windows_condition &); + public: + windows_condition(); + ~windows_condition(); + + void notify_one(); + void notify_all(); + + template <typename L> + bool timed_wait(L& lock, const boost::posix_time::ptime &abs_time) + { + if(abs_time == boost::posix_time::pos_infin){ + this->wait(lock); + return true; + } + if (!lock) + throw lock_exception(); + return this->do_timed_wait(abs_time, *lock.mutex()); + } + + template <typename L, typename Pr> + bool timed_wait(L& lock, const boost::posix_time::ptime &abs_time, Pr pred) + { + if(abs_time == boost::posix_time::pos_infin){ + this->wait(lock, pred); + return true; + } + if (!lock) + throw lock_exception(); + while (!pred()){ + if (!this->do_timed_wait(abs_time, *lock.mutex())) + return pred(); + } + return true; + } + + template <typename L> + void wait(L& lock) + { + if (!lock) + throw lock_exception(); + do_wait(*lock.mutex()); + } + + template <typename L, typename Pr> + void wait(L& lock, Pr pred) + { + if (!lock) + throw lock_exception(); + + while (!pred()) + do_wait(*lock.mutex()); + } + + template<class InterprocessMutex> + void do_wait(InterprocessMutex &mut); + + template<class InterprocessMutex> + bool do_timed_wait(const boost::posix_time::ptime &abs_time, InterprocessMutex &mut); + + private: + + template<class InterprocessMutex> + bool do_timed_wait(bool timeout_enabled, const boost::posix_time::ptime &abs_time, InterprocessMutex &mut); + void do_signal (bool broadcast); + + boost::int32_t m_nwaiters_blocked; + boost::int32_t m_nwaiters_gone; + boost::int32_t m_nwaiters_to_unblock; + windows_semaphore m_sem_block_queue; + windows_semaphore m_sem_block_lock; + windows_mutex m_mtx_unblock_lock; +}; + +inline windows_condition::windows_condition() + : m_nwaiters_blocked(0) + , m_nwaiters_gone(0) + , m_nwaiters_to_unblock(0) + , m_sem_block_queue(0) + , m_sem_block_lock(1) + , m_mtx_unblock_lock() +{} + +inline windows_condition::~windows_condition() +{} + +inline void windows_condition::notify_one() +{ this->do_signal(false); } + +inline void windows_condition::notify_all() +{ this->do_signal(true); } + +inline void windows_condition::do_signal(bool broadcast) +{ + boost::int32_t nsignals_to_issue; + + { + scoped_lock<windows_mutex> locker(m_mtx_unblock_lock); + + if ( 0 != m_nwaiters_to_unblock ) { // the gate is closed!!! + if ( 0 == m_nwaiters_blocked ) { // NO-OP + //locker's destructor triggers m_mtx_unblock_lock.unlock() + return; + } + if (broadcast) { + m_nwaiters_to_unblock += nsignals_to_issue = m_nwaiters_blocked; + m_nwaiters_blocked = 0; + } + else { + nsignals_to_issue = 1; + m_nwaiters_to_unblock++; + m_nwaiters_blocked--; + } + } + else if ( m_nwaiters_blocked > m_nwaiters_gone ) { // HARMLESS RACE CONDITION! + m_sem_block_lock.wait(); // close the gate + if ( 0 != m_nwaiters_gone ) { + m_nwaiters_blocked -= m_nwaiters_gone; + m_nwaiters_gone = 0; + } + if (broadcast) { + nsignals_to_issue = m_nwaiters_to_unblock = m_nwaiters_blocked; + m_nwaiters_blocked = 0; + } + else { + nsignals_to_issue = m_nwaiters_to_unblock = 1; + m_nwaiters_blocked--; + } + } + else { // NO-OP + //locker's destructor triggers m_mtx_unblock_lock.unlock() + return; + } + //locker's destructor triggers m_mtx_unblock_lock.unlock() + } + m_sem_block_queue.post(nsignals_to_issue); +} + +template<class InterprocessMutex> +inline void windows_condition::do_wait(InterprocessMutex &mut) +{ this->do_timed_wait(false, boost::posix_time::ptime(), mut); } + +template<class InterprocessMutex> +inline bool windows_condition::do_timed_wait + (const boost::posix_time::ptime &abs_time, InterprocessMutex &mut) +{ return this->do_timed_wait(true, abs_time, mut); } + +template<class InterprocessMutex> +inline bool windows_condition::do_timed_wait + (bool tout_enabled, const boost::posix_time::ptime &abs_time, InterprocessMutex &mtxExternal) +{ + //Initialize to avoid warnings + boost::int32_t nsignals_was_left = 0; + boost::int32_t nwaiters_was_gone = 0; + + m_sem_block_lock.wait(); + ++m_nwaiters_blocked; + m_sem_block_lock.post(); + + struct scoped_unlock + { + InterprocessMutex & mut; + scoped_unlock(InterprocessMutex & m) + : mut(m) + { m.unlock(); } + + ~scoped_unlock() + { mut.lock(); } + } unlocker(mtxExternal); + + + bool bTimedOut = tout_enabled ? !m_sem_block_queue.timed_wait(abs_time) : (m_sem_block_queue.wait(), false); + + { + scoped_lock<windows_mutex> locker(m_mtx_unblock_lock); + if ( 0 != (nsignals_was_left = m_nwaiters_to_unblock) ) { + if ( bTimedOut ) { // timeout (or canceled) + if ( 0 != m_nwaiters_blocked ) { + m_nwaiters_blocked--; + } + else { + m_nwaiters_gone++; // count spurious wakeups. + } + } + if ( 0 == --m_nwaiters_to_unblock ) { + if ( 0 != m_nwaiters_blocked ) { + m_sem_block_lock.post(); // open the gate. + nsignals_was_left = 0; // do not open the gate below again. + } + else if ( 0 != (nwaiters_was_gone = m_nwaiters_gone) ) { + m_nwaiters_gone = 0; + } + } + } + else if ( (std::numeric_limits<boost::int32_t>::max)()/2 + == ++m_nwaiters_gone ) { // timeout/canceled or spurious semaphore :-) + m_sem_block_lock.wait(); + m_nwaiters_blocked -= m_nwaiters_gone; // something is going on here - test of timeouts? :-) + m_sem_block_lock.post(); + m_nwaiters_gone = 0; + } + //locker's destructor triggers m_mtx_unblock_lock.unlock() + } + + if ( 1 == nsignals_was_left ) { + if ( 0 != nwaiters_was_gone ) { + // sem_adjust( m_sem_block_queue,-nwaiters_was_gone ); + while ( nwaiters_was_gone-- ) { + m_sem_block_queue.wait(); // better now than spurious later + } + } + m_sem_block_lock.post(); // open the gate + } + + //mtxExternal.lock(); called from unlocker + + return ( bTimedOut ) ? false : true; +} + +} //namespace ipcdetail +} //namespace interprocess +} //namespace boost + +#include <boost/interprocess/detail/config_end.hpp> + +#endif //BOOST_INTERPROCESS_DETAIL_WINDOWS_CONDITION_HPP diff --git a/boost/interprocess/sync/windows/mutex.hpp b/boost/interprocess/sync/windows/mutex.hpp new file mode 100644 index 0000000000..5eca522339 --- /dev/null +++ b/boost/interprocess/sync/windows/mutex.hpp @@ -0,0 +1,137 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2005-2011. 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_DETAIL_WINDOWS_MUTEX_HPP +#define BOOST_INTERPROCESS_DETAIL_WINDOWS_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/detail/posix_time_types_wrk.hpp> +#include <boost/interprocess/detail/win32_api.hpp> +#include <boost/interprocess/detail/intermodule_singleton.hpp> +#include <boost/interprocess/sync/windows/sync_utils.hpp> +#include <boost/interprocess/exceptions.hpp> + + +namespace boost { +namespace interprocess { +namespace ipcdetail { + +class windows_mutex +{ + windows_mutex(const windows_mutex &); + windows_mutex &operator=(const windows_mutex &); + public: + + windows_mutex(); + ~windows_mutex(); + + void lock(); + bool try_lock(); + bool timed_lock(const boost::posix_time::ptime &abs_time); + void unlock(); + void take_ownership(){}; + + private: + const sync_id id_; +}; + +inline windows_mutex::windows_mutex() + : id_() +{ + sync_handles &handles = + intermodule_singleton<sync_handles>::get(); + //Create mutex with the initial count + bool open_or_created; + handles.obtain_mutex(this->id_, &open_or_created); + //The mutex must be created, never opened + assert(open_or_created); + assert(open_or_created && winapi::get_last_error() != winapi::error_already_exists); + (void)open_or_created; +} + +inline windows_mutex::~windows_mutex() +{ + sync_handles &handles = + intermodule_singleton<sync_handles>::get(); + handles.destroy_handle(this->id_); +} + +inline void windows_mutex::lock(void) +{ + sync_handles &handles = + intermodule_singleton<sync_handles>::get(); + //This can throw + void *hnd = handles.obtain_mutex(this->id_); + unsigned long ret = winapi::wait_for_single_object(hnd, winapi::infinite_time); + if(ret == winapi::wait_failed){ + error_info err(winapi::get_last_error()); + throw interprocess_exception(err); + } +} + +inline bool windows_mutex::try_lock(void) +{ + sync_handles &handles = + intermodule_singleton<sync_handles>::get(); + //This can throw + void *hnd = handles.obtain_mutex(this->id_); + unsigned long ret = winapi::wait_for_single_object(hnd, 0); + if(ret == winapi::wait_failed){ + error_info err(winapi::get_last_error()); + throw interprocess_exception(err); + } + return ret != winapi::wait_timeout; +} + +inline bool windows_mutex::timed_lock(const boost::posix_time::ptime &abs_time) +{ + if(abs_time == boost::posix_time::pos_infin){ + this->lock(); + return true; + } + boost::posix_time::ptime now + = boost::posix_time::microsec_clock::universal_time(); + + unsigned long ms = (unsigned long)(abs_time-now).total_milliseconds(); + sync_handles &handles = + intermodule_singleton<sync_handles>::get(); + //This can throw + void *hnd = handles.obtain_mutex(this->id_); + unsigned long ret = winapi::wait_for_single_object(hnd, ms); + if(ret == winapi::wait_failed){ + error_info err(winapi::get_last_error()); + throw interprocess_exception(err); + } + return ret != winapi::wait_timeout; +} + +inline void windows_mutex::unlock(void) +{ + sync_handles &handles = + intermodule_singleton<sync_handles>::get(); + //This can throw + void *hnd = handles.obtain_mutex(this->id_); + int ret = winapi::release_mutex(hnd); + (void)ret; + assert(ret); +} + +} //namespace ipcdetail { +} //namespace interprocess { +} //namespace boost { + +#include <boost/interprocess/detail/config_end.hpp> + +#endif //BOOST_INTERPROCESS_DETAIL_WINDOWS_MUTEX_HPP diff --git a/boost/interprocess/sync/windows/recursive_mutex.hpp b/boost/interprocess/sync/windows/recursive_mutex.hpp new file mode 100644 index 0000000000..9565cb6fb4 --- /dev/null +++ b/boost/interprocess/sync/windows/recursive_mutex.hpp @@ -0,0 +1,43 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2005-2011. 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_DETAIL_WINDOWS_RECURSIVE_MUTEX_HPP +#define BOOST_INTERPROCESS_DETAIL_WINDOWS_RECURSIVE_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/windows/mutex.hpp> + +namespace boost { +namespace interprocess { +namespace ipcdetail { + +//Windows mutex is already recursive +class windows_recursive_mutex + : public windows_mutex +{ + windows_recursive_mutex(const windows_recursive_mutex &); + windows_recursive_mutex &operator=(const windows_recursive_mutex &); + public: + windows_recursive_mutex() : windows_mutex() {} +}; + +} //namespace ipcdetail { +} //namespace interprocess { +} //namespace boost { + + +#include <boost/interprocess/detail/config_end.hpp> + +#endif //BOOST_INTERPROCESS_DETAIL_WINDOWS_RECURSIVE_MUTEX_HPP diff --git a/boost/interprocess/sync/windows/semaphore.hpp b/boost/interprocess/sync/windows/semaphore.hpp new file mode 100644 index 0000000000..d5835ceec7 --- /dev/null +++ b/boost/interprocess/sync/windows/semaphore.hpp @@ -0,0 +1,137 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2005-2011. 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_DETAIL_WINDOWS_SEMAPHORE_HPP +#define BOOST_INTERPROCESS_DETAIL_WINDOWS_SEMAPHORE_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/detail/posix_time_types_wrk.hpp> +#include <boost/interprocess/detail/win32_api.hpp> +#include <boost/interprocess/detail/intermodule_singleton.hpp> +#include <boost/interprocess/sync/windows/sync_utils.hpp> +#include <boost/interprocess/exceptions.hpp> + + +namespace boost { +namespace interprocess { +namespace ipcdetail { + +class windows_semaphore +{ + windows_semaphore(const windows_semaphore &); + windows_semaphore &operator=(const windows_semaphore &); + public: + + windows_semaphore(unsigned int initialCount); + ~windows_semaphore(); + + void post(long release_count = 1); + void wait(); + bool try_wait(); + bool timed_wait(const boost::posix_time::ptime &abs_time); + + private: + const sync_id id_; +}; + +inline windows_semaphore::windows_semaphore(unsigned int initialCount) + : id_() +{ + sync_handles &handles = + intermodule_singleton<sync_handles>::get(); + //Force smeaphore creation with the initial count + bool open_or_created; + handles.obtain_semaphore(this->id_, initialCount, &open_or_created); + //The semaphore must be created, never opened + assert(open_or_created); + assert(open_or_created && winapi::get_last_error() != winapi::error_already_exists); + (void)open_or_created; +} + +inline windows_semaphore::~windows_semaphore() +{ + sync_handles &handles = + intermodule_singleton<sync_handles>::get(); + handles.destroy_handle(this->id_); +} + +inline void windows_semaphore::wait(void) +{ + sync_handles &handles = + intermodule_singleton<sync_handles>::get(); + //This can throw + void *hnd = handles.obtain_semaphore(this->id_, 0); + unsigned long ret = winapi::wait_for_single_object(hnd, winapi::infinite_time); + if(ret == winapi::wait_failed){ + error_info err(winapi::get_last_error()); + throw interprocess_exception(err); + } +} + +inline bool windows_semaphore::try_wait(void) +{ + sync_handles &handles = + intermodule_singleton<sync_handles>::get(); + //This can throw + void *hnd = handles.obtain_semaphore(this->id_, 0); + unsigned long ret = winapi::wait_for_single_object(hnd, 0); + if(ret == winapi::wait_failed){ + error_info err(winapi::get_last_error()); + throw interprocess_exception(err); + } + return ret != winapi::wait_timeout; +} + +inline bool windows_semaphore::timed_wait(const boost::posix_time::ptime &abs_time) +{ + if(abs_time == boost::posix_time::pos_infin){ + this->wait(); + return true; + } + boost::posix_time::ptime now + = boost::posix_time::microsec_clock::universal_time(); + + unsigned long ms = (unsigned long)(abs_time-now).total_milliseconds(); + sync_handles &handles = + intermodule_singleton<sync_handles>::get(); + //This can throw + void *hnd = handles.obtain_semaphore(this->id_, 0); + unsigned long ret = winapi::wait_for_single_object(hnd, ms); + if(ret == winapi::wait_failed){ + error_info err(winapi::get_last_error()); + throw interprocess_exception(err); + } + return ret != winapi::wait_timeout; +} + +inline void windows_semaphore::post(long release_count) +{ + sync_handles &handles = + intermodule_singleton<sync_handles>::get(); + //This can throw + void *hnd = handles.obtain_semaphore(this->id_, 0); + long prev_count; + int ret = winapi::release_semaphore(hnd, release_count, &prev_count); + (void)ret; + assert(ret); +} + +} //namespace ipcdetail { +} //namespace interprocess { +} //namespace boost { + +#include <boost/interprocess/detail/config_end.hpp> + +#endif //BOOST_INTERPROCESS_DETAIL_WINDOWS_SEMAPHORE_HPP diff --git a/boost/interprocess/sync/windows/sync_utils.hpp b/boost/interprocess/sync/windows/sync_utils.hpp new file mode 100644 index 0000000000..89c4aeda0b --- /dev/null +++ b/boost/interprocess/sync/windows/sync_utils.hpp @@ -0,0 +1,173 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2005-2011. 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_DETAIL_SYNC_UTILS_HPP +#define BOOST_INTERPROCESS_DETAIL_SYNC_UTILS_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/detail/win32_api.hpp> +#include <boost/interprocess/sync/spin/mutex.hpp> +#include <boost/interprocess/exceptions.hpp> +#include <boost/interprocess/sync/scoped_lock.hpp> +#include <boost/unordered/unordered_map.hpp> +#include <cstddef> + +namespace boost { +namespace interprocess { +namespace ipcdetail { + +inline bool bytes_to_str(const void *mem, const std::size_t mem_length, char *out_str, std::size_t &out_length) +{ + const std::size_t need_mem = mem_length*2+1; + if(out_length < need_mem){ + return false; + } + + const char Characters [] = + { '0', '1', '2', '3', '4', '5', '6', '7' + , '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + + std::size_t char_counter = 0; + const char *buf = (const char *)mem; + for(std::size_t i = 0; i != mem_length; ++i){ + out_str[char_counter++] = Characters[(buf[i]&0xF0)>>4]; + out_str[char_counter++] = Characters[(buf[i]&0x0F)]; + } + out_str[char_counter] = 0; + return true; +} + +struct sync_id +{ + sync_id() + { winapi::query_performance_counter(&rand); } + + __int64 rand; + + friend std::size_t hash_value(const sync_id &m) + { return boost::hash_value(m.rand); } + + friend bool operator==(const sync_id &l, const sync_id &r) + { return l.rand == r.rand; } +}; +/* +#define BOOST_NO_LONG_LONG ss + +#if defined(BOOST_NO_LONG_LONG) + +#error "defined(BOOST_NO_LONG_LONG)" +#else +#error "NOT defined(BOOST_NO_LONG_LONG)" +#endif +*/ +class sync_handles +{ + public: + enum type { MUTEX, SEMAPHORE }; + + private: + typedef boost::unordered_map<sync_id, void*> map_type; + static const std::size_t LengthOfGlobal = sizeof("Global\\boost.ipc")-1; + static const std::size_t StrSize = LengthOfGlobal + (sizeof(sync_id)*2+1); + typedef char NameBuf[StrSize]; + + void fill_name(NameBuf &name, const sync_id &id) + { + const char *n = "Global\\boost.ipc"; + std::size_t i = 0; + do{ + name[i] = n[i]; + ++i; + } while(n[i]); + std::size_t len = sizeof(NameBuf) - LengthOfGlobal; + bytes_to_str(&id.rand, sizeof(id.rand), &name[LengthOfGlobal], len); + } + + void erase_and_throw_if_error(void *hnd_val, const sync_id &id) + { + if(!hnd_val){ + map_.erase(id); + error_info err(winapi::get_last_error()); + throw interprocess_exception(err); + } + } + + void* open_or_create_semaphore(const sync_id &id, unsigned int initial_count) + { + NameBuf name; + fill_name(name, id); + void *hnd_val = winapi::open_or_create_semaphore + (name, (long)initial_count, (long)(((unsigned long)(-1))>>1), unrestricted_security.get_attributes()); + erase_and_throw_if_error(hnd_val, id); + return hnd_val; + } + + void* open_or_create_mutex(const sync_id &id) + { + NameBuf name; + fill_name(name, id); + void *hnd_val = winapi::open_or_create_mutex + (name, false, unrestricted_security.get_attributes()); + erase_and_throw_if_error(hnd_val, id); + return hnd_val; + } + + public: + void *obtain_mutex(const sync_id &id, bool *popen_created = 0) + { + scoped_lock<spin_mutex> lock(mtx_); + void *&hnd_val = map_[id]; + if(!hnd_val){ + hnd_val = open_or_create_mutex(id); + if(popen_created) *popen_created = true; + } + return hnd_val; + } + + void *obtain_semaphore(const sync_id &id, unsigned int initial_count, bool *popen_created = 0) + { + scoped_lock<spin_mutex> lock(mtx_); + void *&hnd_val = map_[id]; + if(!hnd_val){ + hnd_val = open_or_create_semaphore(id, initial_count); + if(popen_created) *popen_created = true; + } + return hnd_val; + } + + void destroy_handle(const sync_id &id) + { + scoped_lock<spin_mutex> lock(mtx_); + map_type::iterator it = map_.find(id); + if(it != map_.end()){ + winapi::close_handle(it->second); + map_.erase(it); + } + } + + private: + winapi::interprocess_all_access_security unrestricted_security; + spin_mutex mtx_; + map_type map_; +}; + + +} //namespace ipcdetail { +} //namespace interprocess { +} //namespace boost { + +#include <boost/interprocess/detail/config_end.hpp> + +#endif //BOOST_INTERPROCESS_DETAIL_SYNC_UTILS_HPP diff --git a/boost/interprocess/sync/xsi/advanced_xsi_semaphore.hpp b/boost/interprocess/sync/xsi/advanced_xsi_semaphore.hpp new file mode 100644 index 0000000000..a0c3a91adf --- /dev/null +++ b/boost/interprocess/sync/xsi/advanced_xsi_semaphore.hpp @@ -0,0 +1,193 @@ +/* + * Provide an simpler and easier to understand interface to the System V + * semaphore system calls. There are 7 routines available to the user: + * + * id = sem_create(key, initval); # create with initial value or open + * id = sem_open(key); # open (must already exist) + * sem_wait(id); # wait = P = down by 1 + * sem_signal(id); # signal = V = up by 1 + * sem_op(id, amount); # wait if (amount < 0) + * # signal if (amount > 0) + * sem_close(id); # close + * sem_rm(id); # remove (delete) + * + * We create and use a 3-member set for the requested semaphore. + * The first member, [0], is the actual semaphore value, and the second + * member, [1], is a counter used to know when all processes have finished + * with the semaphore. The counter is initialized to a large number, + * decremented on every create or open and incremented on every close. + * This way we can use the "adjust" feature provided by System V so that + * any process that exit's without calling sem_close() is accounted + * for. It doesn't help us if the last process does this (as we have + * no way of getting control to remove the semaphore) but it will + * work if any process other than the last does an exit (intentional + * or unintentional). + * The third member, [2], of the semaphore set is used as a lock variable + * to avoid any race conditions in the sem_create() and sem_close() + * functions. + */ + +#ifndef BOOST_INTERPROCESS_SYNC_XSI_ADVANCED_XSI_SEMAPHORE_HPP +#define BOOST_INTERPROCESS_SYNC_XSI_ADVANCED_XSI_SEMAPHORE_HPP + +#include <sys/ipc.h> +#include <sys/sem.h> +#include <errno.h> + +namespace boost { +namespace interprocess { +namespace xsi { + +// Create a semaphore with a specified initial value. +// If the semaphore already exists, we don't initialize it (of course). +// We return the semaphore ID if all OK, else -1. + +inline bool advanced_sem_open_or_create(::key_t key, int initval, int &semid, int perm) +{ + semid = -1; + int id, semval; + union semun { + int val; + ::semid_ds *buf; + ushort *array; + } semctl_arg; + + if (key == IPC_PRIVATE) + return false; //not intended for private semaphores + + else if (key == (::key_t) -1) + return false; //probably an ftok() error by caller + + again: + if ((id = ::semget(key, 3, (perm & 0x01FF) | IPC_CREAT)) < 0) + return false; //permission problem or tables full + + // When the semaphore is created, we know that the value of all + // 3 members is 0. + // Get a lock on the semaphore by waiting for [2] to equal 0, + // then increment it. + // + // There is a race condition here. There is a possibility that + // between the semget() above and the ::semop() below, another + // process can call our sem_close() function which can remove + // the semaphore if that process is the last one using it. + // Therefore, we handle the error condition of an invalid + // semaphore ID specially below, and if it does happen, we just + // go back and create it again. + struct sembuf op_lock[2] = { + {2, 0, 0}, // wait for [2] (lock) to equal 0 + {2, 1, SEM_UNDO} // then increment [2] to 1 - this locks it + // UNDO to release the lock if processes exits + // before explicitly unlocking + }; + + if (::semop(id, &op_lock[0], 2) < 0) { + if (errno == EINVAL) + goto again; + } + + // Get the value of the process counter. If it equals 0, + // then no one has initialized the semaphore yet. + if ((semval = ::semctl(id, 1, GETVAL, 0)) < 0) + return false; + + if (semval == 0) { + // We could initialize by doing a SETALL, but that + // would clear the adjust value that we set when we + // locked the semaphore above. Instead, we'll do 2 + // system calls to initialize [0] and [1]. + semctl_arg.val = initval; + if (::semctl(id, 0, SETVAL, semctl_arg) < 0) + return false; + + semctl_arg.val = 1; + if (::semctl(id, 1, SETVAL, semctl_arg) < 0) + return false; + } + + // Decrement the process counter and then release the lock. + struct sembuf op_unlock[1] = { + 2, -1, 0/*SEM_UNDO*/ // decrement [2] (lock) back to 0 + }; + + if (::semop(id, &op_unlock[0], 1) < 0) + return false; + + semid = id; + return true; +} + +// Open a semaphore that must already exist. +// This function should be used, instead of sem_create(), if the caller +// knows that the semaphore must already exist. For example a client +// from a client-server pair would use this, if its the server's +// responsibility to create the semaphore. +// We return the semaphore ID if all OK, else -1. +/* +inline bool advanced_sem_open(key_t key, int &semid) +{ + semid = -1; + if (key == IPC_PRIVATE) + return false; // not intended for private semaphores + + else if (key == (::key_t) -1) + return false; // probably an ftok() error by caller + + if ((semid = ::semget(key, 3, 0)) < 0) + return false; // doesn't exist, or tables full + + // Decrement the process counter. We don't need a lock + struct sembuf op_open[1] = { + 1, -1, SEM_UNDO // decrement [1] (proc counter) with undo on exit + }; + + if (::semop(id, &op_open[0], 1) < 0) + return false; + + return true; +} +*/ +/**************************************************************************** + * Remove a semaphore. + * This call is intended to be called by a server, for example, + * when it is being shut down, as we do an IPC_RMID on the semaphore, + * regardless whether other processes may be using it or not. + * Most other processes should use sem_close() below. + */ + +inline bool advanced_sem_rm(int id) +{ + if (::semctl(id, 0, IPC_RMID, 0) < 0) + return false; + return true; +} + + +/**************************************************************************** + * General semaphore operation. Increment or decrement by a user-specified + * amount (positive or negative; amount can't be zero). + */ + +inline bool advanced_sem_op(int id, int value, bool undo = true) +{ + ::sembuf op_op[1] = { + 0, 99, 0 // decrement or increment [0] with undo on exit + // the 99 is set to the actual amount to add + // or subtract (positive or negative) + }; + if(undo){ + op_op[0].sem_flg = SEM_UNDO; + } + if ((op_op[0].sem_op = value) == 0) + return false; + + if (::semop(id, &op_op[0], 1) < 0) + return false; + return true; +} + +} //namespace xsi { +} //namespace interprocess { +} //namespace boost { + +#endif //BOOST_INTERPROCESS_SYNC_XSI_ADVANCED_XSI_SEMAPHORE_HPP diff --git a/boost/interprocess/sync/xsi/simple_xsi_semaphore.hpp b/boost/interprocess/sync/xsi/simple_xsi_semaphore.hpp new file mode 100644 index 0000000000..266e7c5dac --- /dev/null +++ b/boost/interprocess/sync/xsi/simple_xsi_semaphore.hpp @@ -0,0 +1,116 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2011-2011. 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_SYNC_XSI_SIMPLE_XSI_SEMAPHORE_HPP +#define BOOST_INTERPROCESS_SYNC_XSI_SIMPLE_XSI_SEMAPHORE_HPP + +/* + * Provide an simpler and easier to understand interface to the System V + * semaphore system calls. There are 7 routines available to the user: + * + * id = sem_create(key, initval); # create with initial value or open + * id = sem_open(key); # open (must already exist) + * sem_wait(id); # wait = P = down by 1 + * sem_signal(id); # signal = V = up by 1 + * sem_op(id, amount); # wait if (amount < 0) + * # signal if (amount > 0) + * sem_close(id); # close + * sem_rm(id); # remove (delete) + * + * We create and use a 3-member set for the requested semaphore. + * The first member, [0], is the actual semaphore value, and the second + * member, [1], is a counter used to know when all processes have finished + * with the semaphore. The counter is initialized to a large number, + * decremented on every create or open and incremented on every close. + * This way we can use the "adjust" feature provided by System V so that + * any process that exit's without calling sem_close() is accounted + * for. It doesn't help us if the last process does this (as we have + * no way of getting control to remove the semaphore) but it will + * work if any process other than the last does an exit (intentional + * or unintentional). + * The third member, [2], of the semaphore set is used as a lock variable + * to avoid any race conditions in the sem_create() and sem_close() + * functions. + */ + +#include <sys/ipc.h> +#include <sys/sem.h> +#include <errno.h> + +namespace boost { +namespace interprocess { +namespace xsi { + +// Create a semaphore with a specified initial value. +// If the semaphore already exists, we don't initialize it (of course). +// We return the semaphore ID if all OK, else -1. + +inline bool simple_sem_open_or_create(::key_t key, int initval, int &semid, int perm) +{ + int id, semval; + semid = -1; + + if (key == IPC_PRIVATE) + return false; //not intended for private semaphores + + else if (key == (::key_t) -1) + return false; //probably an ftok() error by caller + + again: + if ((id = ::semget(key, 1, (perm & 0x01FF) | IPC_CREAT)) < 0) + return false; //permission problem or tables full + + semid = id; + return true; +} + +/**************************************************************************** + * Remove a semaphore. + * This call is intended to be called by a server, for example, + * when it is being shut down, as we do an IPC_RMID on the semaphore, + * regardless whether other processes may be using it or not. + * Most other processes should use sem_close() below. + */ + +inline bool simple_sem_rm(int id) +{ + if (::semctl(id, 0, IPC_RMID, 0) < 0) + return false; + return true; +} + + +/**************************************************************************** + * General semaphore operation. Increment or decrement by a user-specified + * amount (positive or negative; amount can't be zero). + */ + +inline bool simple_sem_op(int id, int value, bool undo = true) +{ + ::sembuf op_op[1] = { + 0, 99, 0 // decrement or increment [0] with undo on exit + // the 99 is set to the actual amount to add + // or subtract (positive or negative) + }; + if(undo){ + op_op[0].sem_flg = SEM_UNDO; + } + if ((op_op[0].sem_op = value) == 0) + return false; + + if (::semop(id, &op_op[0], 1) < 0) + return false; + return true; +} + +} //namespace xsi { +} //namespace interprocess { +} //namespace boost { + +#endif //BOOST_INTERPROCESS_SYNC_XSI_SIMPLE_XSI_SEMAPHORE_HPP diff --git a/boost/interprocess/sync/xsi/xsi_named_mutex.hpp b/boost/interprocess/sync/xsi/xsi_named_mutex.hpp new file mode 100644 index 0000000000..3dffdcc2ed --- /dev/null +++ b/boost/interprocess/sync/xsi/xsi_named_mutex.hpp @@ -0,0 +1,228 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2009-2011. 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_XSI_XSI_NAMED_MUTEX_HPP +#define BOOST_INTERPROCESS_XSI_XSI_NAMED_MUTEX_HPP + +#include <boost/interprocess/detail/config_begin.hpp> +#include <boost/interprocess/detail/workaround.hpp> + +#if defined(BOOST_INTERPROCESS_WINDOWS) +#error "This header can't be used in Windows operating systems" +#endif + +#include <boost/move/move.hpp> +#include <boost/interprocess/creation_tags.hpp> +#include <boost/interprocess/exceptions.hpp> +#include <boost/interprocess/detail/utilities.hpp> +#include <boost/interprocess/detail/os_file_functions.hpp> +#include <boost/interprocess/interprocess_fwd.hpp> +#include <boost/interprocess/exceptions.hpp> +#include <boost/interprocess/sync/xsi/basic_xsi_semaphore.hpp> +#include <cstddef> +#include <boost/assert.hpp> +#include <boost/cstdint.hpp> +#include <string> +#include <boost/assert.hpp> + +//!\file +//!Describes a class representing a xsi-based named_mutex. + +namespace boost { +namespace interprocess { + +//!A class that wraps a XSI (System V)-based named semaphore +//!that undoes the operation if the process crashes. +class xsi_named_mutex +{ + /// @cond + //Non-copyable and non-assignable + xsi_named_mutex(xsi_named_mutex &); + xsi_named_mutex &operator=(xsi_named_mutex &); + /// @endcond + + public: + BOOST_MOVABLE_BUT_NOT_COPYABLE(xsi_named_mutex) + + //!Default constructor. + //!Represents an empty xsi_named_mutex. + xsi_named_mutex(); + + //!Tries to create a new XSI-based named mutex with a key obtained from a call to ftok (with path + //!"path" and id "id"), and permissions "perm". + //!If the named mutex previously exists, it tries to open it. + //!Otherwise throws an error. + xsi_named_mutex(open_or_create_t, const char *path, boost::uint8_t id, int perm = 0666) + { this->priv_open_or_create(ipcdetail::DoOpenOrCreate, path, id, perm); } + + //!Moves the ownership of "moved"'s named mutex to *this. + //!After the call, "moved" does not represent any named mutex + //!Does not throw + xsi_named_mutex(BOOST_RV_REF(xsi_named_mutex) moved) + { this->swap(moved); } + + //!Moves the ownership of "moved"'s named mutex to *this. + //!After the call, "moved" does not represent any named mutex. + //!Does not throw + xsi_named_mutex &operator=(BOOST_RV_REF(xsi_named_mutex) moved) + { + xsi_named_mutex tmp(boost::move(moved)); + this->swap(tmp); + return *this; + } + + //!Swaps two xsi_named_mutex. Does not throw + void swap(xsi_named_mutex &other); + + //!Destroys *this. The named mutex is still valid after + //!destruction. use remove() to destroy the named mutex. + ~xsi_named_mutex(); + + //!Returns the path used to construct the + //!named mutex. + const char *get_path() const; + + //!Returns access + //!permissions + int get_permissions() const; + + //!Returns the mapping handle. + //!Never throws + mapping_handle_t get_mapping_handle() const; + + //!Erases a XSI-based named mutex from the system. + //!Returns false on error. Never throws + bool remove(); + + void lock(); + + void unlock(); + + /// @cond + private: + + //!Closes a previously opened file mapping. Never throws. + void priv_close(); + + //!Closes a previously opened file mapping. Never throws. + bool priv_open_or_create( ipcdetail::create_enum_t type + , const char *path + , boost::uint8_t id + , int perm); + int m_semid; + key_t m_key; + boost::uint8_t m_id; + int m_perm; + std::string m_path; + /// @endcond +}; + +/// @cond + +inline xsi_named_mutex::xsi_named_mutex() + : m_semid(-1), m_key(-1), m_id(0), m_perm(0), m_path() +{} + +inline xsi_named_mutex::~xsi_named_mutex() +{ this->priv_close(); } + +inline const char *xsi_named_mutex::get_path() const +{ return m_path.c_str(); } + +inline void xsi_named_mutex::swap(xsi_named_mutex &other) +{ + std::swap(m_key, other.m_key); + std::swap(m_id, other.m_id); + std::swap(m_semid, other.m_semid); + std::swap(m_perm, other.m_perm); + m_path.swap(other.m_path); +} + +inline mapping_handle_t xsi_named_mutex::get_mapping_handle() const +{ mapping_handle_t mhnd = { m_semid, true}; return mhnd; } + +inline int xsi_named_mutex::get_permissions() const +{ return m_perm; } + +inline bool xsi_named_mutex::priv_open_or_create + (ipcdetail::create_enum_t type, const char *path, boost::uint8_t id, int perm) +{ + key_t key; + if(path){ + key = ::ftok(path, id); + if(((key_t)-1) == key){ + error_info err = system_error_code(); + throw interprocess_exception(err); + } + } + else{ + key = IPC_PRIVATE; + } + + perm &= 0x01FF; + + int semid; + if(!xsi::simple_sem_open_or_create(key, 1, semid, perm)){ + error_info err = system_error_code(); + throw interprocess_exception(err); + } + + m_perm = perm; + m_semid = semid; + m_path = path ? path : ""; + m_id = id; + m_key = key; + + return true; +} + +inline void xsi_named_mutex::priv_close() +{ +} + +inline void xsi_named_mutex::lock() +{ + if(!xsi::simple_sem_op(m_semid, -1)){ + error_info err = system_error_code(); + throw interprocess_exception(err); + } +} + +inline void xsi_named_mutex::unlock() +{ + bool success = xsi::simple_sem_op(m_semid, 1); + (void)success; + BOOST_ASSERT(success); +} + +inline bool xsi_named_mutex::remove() +{ + if(m_semid != -1){ + int ret = ::semctl(m_semid, IPC_RMID, 0); + if(-1 == ret) + return false; + //Now put it in default-constructed state + m_semid = -1; + m_key = -1; + m_id = 0; + m_perm = 0; + m_path.clear(); + } + return false; +} + +///@endcond + +} //namespace interprocess { +} //namespace boost { + +#include <boost/interprocess/detail/config_end.hpp> + +#endif //BOOST_INTERPROCESS_XSI_XSI_NAMED_MUTEX_HPP |