1 /* 2 * Distributed under the Boost Software License, Version 1.0. 3 * (See accompanying file LICENSE_1_0.txt or copy at 4 * http://www.boost.org/LICENSE_1_0.txt) 5 * 6 * (C) Copyright 2007-2008 Anthony Williams 7 * (C) Copyright 2012-2013 Vicente J. Botet Escriba 8 * (C) Copyright 2013 Andrey Semashev 9 */ 10 /*! 11 * \file detail/mutexes/timed_mutex_windows.hpp 12 * 13 * \brief This header is the Boost.Sync library implementation, see the library documentation 14 * at http://www.boost.org/doc/libs/release/libs/sync/doc/html/index.html. 15 */ 16 17 #ifndef BOOST_SYNC_DETAIL_MUTEXES_TIMED_MUTEX_WINDOWS_HPP_INCLUDED_ 18 #define BOOST_SYNC_DETAIL_MUTEXES_TIMED_MUTEX_WINDOWS_HPP_INCLUDED_ 19 20 #include <cstddef> 21 #include <boost/cstdint.hpp> 22 #include <boost/assert.hpp> 23 #include <boost/utility/enable_if.hpp> 24 #include <boost/detail/winapi/synchronization.hpp> 25 #include <boost/sync/exceptions/lock_error.hpp> 26 #include <boost/sync/detail/config.hpp> 27 #include <boost/sync/detail/throw_exception.hpp> 28 #include <boost/sync/detail/interlocked.hpp> 29 #include <boost/sync/detail/time_traits.hpp> 30 #include <boost/sync/detail/time_units.hpp> 31 #include <boost/sync/detail/waitable_timer.hpp> 32 #include <boost/sync/detail/mutexes/basic_mutex_windows.hpp> 33 34 #include <boost/sync/detail/header.hpp> 35 36 #ifdef BOOST_HAS_PRAGMA_ONCE 37 #pragma once 38 #endif 39 40 namespace boost { 41 42 namespace sync { 43 44 BOOST_SYNC_DETAIL_OPEN_ABI_NAMESPACE { 45 46 class timed_mutex 47 { 48 public: 49 typedef void _is_condition_variable_compatible; 50 51 private: 52 sync::detail::windows::basic_mutex m_mutex; 53 54 public: 55 #if !defined(BOOST_NO_CXX11_CONSTEXPR) 56 #define BOOST_SYNC_DEFINES_TIMED_MUTEX_CONSTEXPR_CONSTRUCTOR 57 #endif 58 59 BOOST_CONSTEXPR timed_mutex() BOOST_NOEXCEPT : m_mutex() 60 { 61 } 62 63 void lock() 64 { 65 m_mutex.lock(); 66 } 67 68 void unlock() BOOST_NOEXCEPT 69 { 70 m_mutex.unlock(); 71 } 72 73 bool try_lock() 74 { 75 return m_mutex.try_lock(); 76 } 77 78 template< typename Time > 79 typename enable_if_c< sync::detail::time_traits< Time >::is_specialized, bool >::type timed_lock(Time const& t) 80 { 81 if (m_mutex.try_lock()) 82 return true; 83 84 return priv_timed_lock(sync::detail::time_traits< Time >::to_sync_unit(t)); 85 } 86 87 template< typename Duration > 88 typename detail::enable_if_tag< Duration, detail::time_duration_tag, bool >::type try_lock_for(Duration const& rel_time) 89 { 90 if (m_mutex.try_lock()) 91 return true; 92 93 return priv_timed_lock(sync::detail::time_traits< Duration >::to_sync_unit(rel_time)); 94 } 95 96 template< typename TimePoint > 97 typename detail::enable_if_tag< TimePoint, detail::time_point_tag, bool >::type try_lock_until(TimePoint const& abs_time) 98 { 99 if (m_mutex.try_lock()) 100 return true; 101 102 return priv_timed_lock(sync::detail::time_traits< TimePoint >::to_sync_unit(abs_time)); 103 } 104 105 BOOST_DELETED_FUNCTION(timed_mutex(timed_mutex const&)) 106 BOOST_DELETED_FUNCTION(timed_mutex& operator= (timed_mutex const&)) 107 108 private: 109 bool priv_timed_lock(sync::detail::system_time_point const& t) 110 { 111 long old_count = m_mutex.m_active_count; 112 m_mutex.mark_waiting_and_try_lock(old_count); 113 if ((old_count & sync::detail::windows::basic_mutex::lock_flag_value) == 0) 114 return true; 115 116 try 117 { 118 boost::detail::winapi::HANDLE_ handles[2]; 119 handles[0] = m_mutex.get_event(); 120 handles[1] = sync::detail::windows::get_waitable_timer(); 121 122 if (!boost::detail::winapi::SetWaitableTimer(handles[1], reinterpret_cast< const boost::detail::winapi::LARGE_INTEGER_* >(&t.get()), 0, NULL, NULL, false)) 123 { 124 const boost::detail::winapi::DWORD_ err = boost::detail::winapi::GetLastError(); 125 BOOST_SYNC_DETAIL_THROW(lock_error, (err)("timed_mutex::timed_lock failed to set a timeout")); 126 } 127 128 while (true) 129 { 130 const boost::detail::winapi::DWORD_ res = boost::detail::winapi::WaitForMultipleObjects(sizeof(handles) / sizeof(*handles), handles, false, boost::detail::winapi::infinite); 131 if (res == boost::detail::winapi::wait_failed) 132 { 133 const boost::detail::winapi::DWORD_ err = boost::detail::winapi::GetLastError(); 134 BOOST_SYNC_DETAIL_THROW(lock_error, (err)("timed_mutex::timed_lock failed in WaitForMultipleObjects")); 135 } 136 137 switch (res) 138 { 139 case boost::detail::winapi::wait_object_0: // event was notified 140 m_mutex.clear_waiting_and_try_lock(old_count); 141 if ((old_count & sync::detail::windows::basic_mutex::lock_flag_value) == 0) 142 return true; 143 break; 144 145 case boost::detail::winapi::wait_object_0 + 1: // timeout has expired 146 BOOST_ATOMIC_INTERLOCKED_EXCHANGE_ADD(&m_mutex.m_active_count, -1); 147 return false; 148 149 default: 150 BOOST_ASSERT(false); 151 } 152 } 153 } 154 catch (...) 155 { 156 BOOST_ATOMIC_INTERLOCKED_EXCHANGE_ADD(&m_mutex.m_active_count, -1); 157 throw; 158 } 159 } 160 161 bool priv_timed_lock(sync::detail::system_duration t) 162 { 163 long old_count = m_mutex.m_active_count; 164 m_mutex.mark_waiting_and_try_lock(old_count); 165 if ((old_count & sync::detail::windows::basic_mutex::lock_flag_value) == 0) 166 return true; 167 168 try 169 { 170 const boost::detail::winapi::HANDLE_ evt = m_mutex.get_event(); 171 sync::detail::system_duration::native_type time_left = t.get(); 172 while (time_left > 0) 173 { 174 const boost::detail::winapi::DWORD_ dur = time_left > boost::detail::winapi::max_non_infinite_wait ? 175 boost::detail::winapi::max_non_infinite_wait : static_cast< boost::detail::winapi::DWORD_ >(time_left); 176 const boost::detail::winapi::DWORD_ res = boost::detail::winapi::WaitForSingleObject(evt, dur); 177 switch (res) 178 { 179 case boost::detail::winapi::wait_object_0: 180 m_mutex.clear_waiting_and_try_lock(old_count); 181 if ((old_count & sync::detail::windows::basic_mutex::lock_flag_value) == 0) 182 return true; 183 break; 184 185 case boost::detail::winapi::wait_timeout: 186 time_left -= dur; 187 break; 188 189 default: 190 { 191 const boost::detail::winapi::DWORD_ err = boost::detail::winapi::GetLastError(); 192 BOOST_SYNC_DETAIL_THROW(lock_error, (err)("timed_mutex::timed_lock failed in WaitForSingleObject")); 193 } 194 } 195 } 196 197 BOOST_ATOMIC_INTERLOCKED_EXCHANGE_ADD(&m_mutex.m_active_count, -1); 198 199 return false; 200 } 201 catch (...) 202 { 203 BOOST_ATOMIC_INTERLOCKED_EXCHANGE_ADD(&m_mutex.m_active_count, -1); 204 throw; 205 } 206 } 207 208 template< typename TimePoint > 209 bool priv_timed_lock(sync::detail::chrono_time_point< TimePoint > const& t) 210 { 211 typedef TimePoint time_point; 212 typedef typename time_point::clock clock; 213 typedef typename time_point::duration duration; 214 time_point now = clock::now(); 215 while (now < t.get()) 216 { 217 if (priv_timed_lock(sync::detail::time_traits< duration >::to_sync_unit(t.get() - now))) 218 return true; 219 now = clock::now(); 220 } 221 return false; 222 } 223 }; 224 225 } // namespace winnt 226 227 } // namespace sync 228 229 } // namespace boost 230 231 #include <boost/sync/detail/footer.hpp> 232 233 #endif // BOOST_SYNC_DETAIL_MUTEXES_TIMED_MUTEX_WINDOWS_HPP_INCLUDED_ 234