Index: sync/interprocess_condition.hpp =================================================================== --- sync/interprocess_condition.hpp (revision 42159) +++ sync/interprocess_condition.hpp (working copy) @@ -33,6 +33,9 @@ #include #include #define BOOST_INTERPROCESS_USE_POSIX +#elif !defined(BOOST_INTERPROCESS_FORCE_GENERIC_EMULATION) && defined(BOOST_INTERPROCESS_WINDOWS) + #include + #define BOOST_INTERPROCESS_USE_WINDOWS #else #include #include @@ -146,6 +149,19 @@ bool do_timed_wait(const boost::posix_time::ptime &abs_time, interprocess_mutex &mut); + #if defined BOOST_INTERPROCESS_USE_WINDOWS + // TODO: Investigate native condition variables that Vista+ supports. + // Probably not usable because "cannot be shared across processes". +#define WIN32_POSIX_SEMANTICS 1 + interprocess_mutex m_internal_mutex; + interprocess_mutex m_signaler_mutex; + interprocess_mutex m_num_waiters_mutex; + HANDLE m_signal_event; + HANDLE m_waiters_done_signal; + boost::uint32_t m_num_waiters; + bool m_is_notify_all; + #endif + #if defined (BOOST_INTERPROCESS_USE_GENERIC_EMULATION) enum { SLEEP = 0, NOTIFY_ONE, NOTIFY_ALL }; interprocess_mutex m_enter_mut; @@ -164,6 +180,11 @@ } // namespace boost +#ifdef BOOST_INTERPROCESS_USE_WINDOWS +# undef BOOST_INTERPROCESS_USE_WINDOWS +# include +#endif + #ifdef BOOST_INTERPROCESS_USE_GENERIC_EMULATION # undef BOOST_INTERPROCESS_USE_GENERIC_EMULATION # include Index: sync/interprocess_mutex.hpp =================================================================== --- sync/interprocess_mutex.hpp (revision 42159) +++ sync/interprocess_mutex.hpp (working copy) @@ -31,6 +31,10 @@ #include #include #define BOOST_INTERPROCESS_USE_POSIX +#elif !defined(BOOST_INTERPROCESS_FORCE_GENERIC_EMULATION) && defined(BOOST_INTERPROCESS_WINDOWS) + #define BOOST_INTERPROCESS_USE_WINDOWS + #include + #include #else #include #define BOOST_INTERPROCESS_USE_GENERIC_EMULATION @@ -107,6 +111,10 @@ /// @cond private: + #if defined(BOOST_INTERPROCESS_USE_WINDOWS) + std::string m_mutex_name; // interprocess mutexes on Windows must be named + #endif + #if defined(BOOST_INTERPROCESS_USE_GENERIC_EMULATION) friend class detail::robust_emulation_helpers::mutex_traits; void take_ownership(){ mutex.take_ownership(); } @@ -143,6 +151,11 @@ # undef BOOST_INTERPROCESS_USE_POSIX #endif +#ifdef BOOST_INTERPROCESS_USE_WINDOWS +#include +# undef BOOST_INTERPROCESS_USE_WINDOWS +#endif + #include #endif //BOOST_INTERPROCESS_MUTEX_HPP Index: sync/win32/interprocess_condition.hpp =================================================================== --- sync/win32/interprocess_condition.hpp (revision 0) +++ sync/win32/interprocess_condition.hpp (revision 0) @@ -0,0 +1,232 @@ + +////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2011 +// Daniel W. Brown +// +// 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. Daniel W. Brown makes no representations +// about the suitability of this software for any purpose. +// It is provided "as is" without express or implied warranty. +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// +// TODO: Optimization. Vista+ has native condition variables. +// +// + +#ifndef BOOST_INTERPROCESS_WIN32_CONDITION_HPP +#define BOOST_INTERPROCESS_WIN32_CONDITION_HPP + +#if (defined _MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif + +#include + +namespace boost { + +namespace interprocess { + + +inline interprocess_condition::interprocess_condition() + : m_internal_mutex(), m_signaler_mutex(), m_num_waiters_mutex(), + m_num_waiters(0), m_is_notify_all(false) +{ + m_waiters_done_signal = CreateEvent(NULL, false, false, NULL); + if (m_waiters_done_signal == NULL) + throw interprocess_exception("CreateEvent failed"); + + m_signal_event = CreateEvent(NULL, false, false, NULL); + if (m_signal_event == NULL) + throw interprocess_exception("CreateEvent failed"); + +} + + +inline interprocess_condition::~interprocess_condition() +{ + CloseHandle(m_waiters_done_signal); + CloseHandle(m_signal_event); +} + + +inline void interprocess_condition::notify_one() +{ +#if WIN32_POSIX_SEMANTICS + scoped_lock s(m_signaler_mutex); + + m_num_waiters_mutex.lock(); +#endif + + m_is_notify_all = false; + if (m_num_waiters > 0) + { + SetEvent(m_signal_event); +#if WIN32_POSIX_SEMANTICS + m_num_waiters_mutex.unlock(); +#endif + WaitForSingleObject(m_waiters_done_signal, INFINITE); + } + else + { +#if WIN32_POSIX_SEMANTICS + m_num_waiters_mutex.unlock(); +#endif + } +} + + +inline void interprocess_condition::notify_all() +{ +#if WIN32_POSIX_SEMANTICS + scoped_lock s(m_signaler_mutex); + + m_num_waiters_mutex.lock(); +#endif + + m_is_notify_all = true; + if (m_num_waiters > 0) + { + SetEvent(m_signal_event); +#if WIN32_POSIX_SEMANTICS + m_num_waiters_mutex.unlock(); +#endif + WaitForSingleObject(m_waiters_done_signal, INFINITE); + } + else + { +#if WIN32_POSIX_SEMANTICS + m_num_waiters_mutex.unlock(); +#endif + } +} + + +inline void interprocess_condition::do_wait(interprocess_mutex &mut) +{ +#if WIN32_POSIX_SEMANTICS + m_num_waiters_mutex.lock(); +#endif + + m_num_waiters++; + + // Releasing m_mutex allows signaler to set m_signal_event + mut.unlock(); + + m_internal_mutex.lock(); + +#if WIN32_POSIX_SEMANTICS + m_num_waiters_mutex.unlock(); +#endif + + // primary cond_var wait + WaitForSingleObject(m_signal_event, INFINITE); + + // No need for m_num_waiters lock here. + // Guaranteed that only one waiter received m_signal_event + // since m_internal_mutex is held. + m_num_waiters--; + + if (m_is_notify_all) + { + if (m_num_waiters == 0) + { + // Wake the signaler + SetEvent(m_waiters_done_signal); + } + else + { + // Wake another waiter + SetEvent(m_signal_event); + } + } + else + { + // Wake the signaler + SetEvent(m_waiters_done_signal); + } + + m_internal_mutex.unlock(); + + mut.lock(); +} + + +inline bool interprocess_condition::do_timed_wait(const boost::posix_time::ptime &abs_time, interprocess_mutex &mut) +{ +#if WIN32_POSIX_SEMANTICS + m_num_waiters_mutex.lock(); +#endif + + if (abs_time == boost::posix_time::pos_infin) { + do_wait(mut); + } + + m_num_waiters++; + + // Releasing m_mutex allows signaller to set m_signal_event + mut.unlock(); + + boost::posix_time::ptime now(microsec_clock::local_time()); + boost::posix_time::time_duration duration(abs_time - now); + + m_internal_mutex.lock(); + +#if WIN32_POSIX_SEMANTICS + m_num_waiters_mutex.unlock(); +#endif + + int rv = WaitForSingleObject(m_signal_event, duration.total_milliseconds()); + + switch(rv) + { + case WAIT_OBJECT_0: + case WAIT_OBJECT_0+1: + break; + + case WAIT_TIMEOUT: + m_num_waiters--; + return false; + + default: + // Shouldn't get here + m_num_waiters--; + throw interprocess_exception("WaitForMultipleObjects failed"); + } + + m_num_waiters--; + + if (m_is_notify_all) + { + if (m_num_waiters == 0) + { + // Wake the signaler + SetEvent(m_waiters_done_signal); + } + else + { + // Wake another waiter + SetEvent(m_signal_event); + } + } + else + { + // Wake the signaler + SetEvent(m_waiters_done_signal); + } + + m_internal_mutex.unlock(); + + mut.lock(); + + return true; +} + +} // namespace interprocess +} // namespace boost + +#endif \ No newline at end of file Index: sync/win32/interprocess_mutex.hpp =================================================================== --- sync/win32/interprocess_mutex.hpp (revision 0) +++ sync/win32/interprocess_mutex.hpp (revision 0) @@ -0,0 +1,171 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2011 +// Daniel W. Brown +// +// 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. Daniel W. Brown 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_WIN32_MUTEX_HPP +#define BOOST_INTERPROCESS_WIN32_MUTEX_HPP + +#if (defined _MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif + +#include + +#include +#include +#include + +namespace boost { +namespace interprocess { + +static std::string randomString() +{ + std::stringbuf rndStr; + std::string chars( + "abcdefghijklmnopqrstuvwxyz" + "1234567890" + ); + + for(int i = 0; i < 8; ++i) { + rndStr.sputc(chars[rand() % chars.size()]); + } + + return rndStr.str(); +} + +inline interprocess_mutex::interprocess_mutex() +{ + HANDLE mutex; + + // Create a mutex with a unique name + srand(time(NULL)); + do + { + m_mutex_name = randomString(); + mutex = CreateMutex(NULL, FALSE, m_mutex_name.c_str()); + } + while (GetLastError() == ERROR_ALREADY_EXISTS); + + if (mutex == NULL) + throw interprocess_exception("CreateMutex failed"); +} + +inline interprocess_mutex::~interprocess_mutex() +{ + // Lookup the interprocess mutex by name + HANDLE mutex = CreateMutex(NULL, FALSE, m_mutex_name.c_str()); + + if (mutex == NULL || GetLastError() != ERROR_ALREADY_EXISTS) + { + // Shouldn't get here + throw interprocess_exception("CreateMutex failed"); + } + + CloseHandle(mutex); +} + +inline void interprocess_mutex::lock() +{ + // Lookup the interprocess mutex by name + HANDLE mutex = CreateMutex(NULL, FALSE, m_mutex_name.c_str()); + + if (mutex == NULL || GetLastError() != ERROR_ALREADY_EXISTS) + { + // Shouldn't get here + throw interprocess_exception("CreateMutex failed"); + } + + DWORD rv = WaitForSingleObject(mutex, INFINITE); + + switch(rv) + { + case WAIT_OBJECT_0: + case WAIT_ABANDONED: + break; + + default: + // Shouldn't get here + throw interprocess_exception("WaitForSingleObject failed"); + } +} + +inline bool interprocess_mutex::try_lock() +{ + // Lookup the interprocess mutex by name + HANDLE mutex = CreateMutex(NULL, FALSE, m_mutex_name.c_str()); + + if (mutex == NULL || GetLastError() != ERROR_ALREADY_EXISTS) + { + // Shouldn't get here + throw interprocess_exception("CreateMutex failed"); + } + + DWORD rv = WaitForSingleObject(mutex, 0); + + switch(rv) + { + case WAIT_OBJECT_0: + case WAIT_ABANDONED: + return true; + + case WAIT_TIMEOUT: + return false; + + default: + throw interprocess_exception("WaitForSingleObject failed"); + } +} + +inline bool interprocess_mutex::timed_lock(const boost::posix_time::ptime &abs_time) +{ + if(abs_time == boost::posix_time::pos_infin){ + this->lock(); + return true; + } + + // Lookup the interprocess mutex by name + HANDLE mutex = CreateMutex(NULL, FALSE, m_mutex_name.c_str()); + + if (mutex == NULL || GetLastError() != ERROR_ALREADY_EXISTS) + { + // Shouldn't get here + throw interprocess_exception("CreateMutex failed"); + } + + boost::posix_time::ptime now(microsec_clock::local_time()); + boost::posix_time::time_duration duration(abs_time - now); + + return WaitForSingleObject(mutex, duration.total_milliseconds()) == ERROR_SUCCESS; +} + +inline void interprocess_mutex::unlock() +{ + // Lookup the interprocess mutex by name + HANDLE mutex = CreateMutex(NULL, FALSE, m_mutex_name.c_str()); + + if (mutex == NULL || GetLastError() != ERROR_ALREADY_EXISTS) + { + // Shouldn't get here + throw interprocess_exception("CreateMutex failed"); + } + + ReleaseMutex(mutex); +} + + +} // namespace interprocess +} // namespace boost + + +#endif // #ifndef BOOST_INTERPROCESS_WIN32_MUTEX_HPP \ No newline at end of file