1 #ifndef BOOST_BASIC_TIMED_MUTEX_WIN32_HPP 2 #define BOOST_BASIC_TIMED_MUTEX_WIN32_HPP 3 4 // basic_timed_mutex_win32.hpp 5 // 6 // (C) Copyright 2006-8 Anthony Williams 7 // (C) Copyright 2011-2012 Vicente J. Botet Escriba 8 // 9 // Distributed under the Boost Software License, Version 1.0. (See 10 // accompanying file LICENSE_1_0.txt or copy at 11 // http://www.boost.org/LICENSE_1_0.txt) 12 13 #include <boost/assert.hpp> 14 #include <boost/thread/win32/thread_primitives.hpp> 15 #include <boost/thread/win32/interlocked_read.hpp> 16 #include <boost/thread/thread_time.hpp> 17 #if defined BOOST_THREAD_USES_DATETIME 18 #include <boost/thread/xtime.hpp> 19 #endif 20 #include <boost/detail/interlocked.hpp> 21 #ifdef BOOST_THREAD_USES_CHRONO 22 #include <boost/chrono/system_clocks.hpp> 23 #include <boost/chrono/ceil.hpp> 24 #endif 25 #include <boost/thread/detail/platform_time.hpp> 26 27 #include <boost/config/abi_prefix.hpp> 28 29 namespace boost 30 { 31 namespace detail 32 { 33 struct basic_timed_mutex 34 { 35 BOOST_STATIC_CONSTANT(unsigned char,lock_flag_bit=31); 36 BOOST_STATIC_CONSTANT(unsigned char,event_set_flag_bit=30); 37 BOOST_STATIC_CONSTANT(long,lock_flag_value=1<<lock_flag_bit); 38 BOOST_STATIC_CONSTANT(long,event_set_flag_value=1<<event_set_flag_bit); 39 long active_count; 40 void* event; 41 initializeboost::detail::basic_timed_mutex42 void initialize() 43 { 44 active_count=0; 45 event=0; 46 } 47 destroyboost::detail::basic_timed_mutex48 void destroy() 49 { 50 #ifdef BOOST_MSVC 51 #pragma warning(push) 52 #pragma warning(disable:4312) 53 #endif 54 void* const old_event=BOOST_INTERLOCKED_EXCHANGE_POINTER(&event,0); 55 #ifdef BOOST_MSVC 56 #pragma warning(pop) 57 #endif 58 if(old_event) 59 { 60 winapi::CloseHandle(old_event); 61 } 62 } 63 64 // Take the lock flag if it's available try_lockboost::detail::basic_timed_mutex65 bool try_lock() BOOST_NOEXCEPT 66 { 67 return !win32::interlocked_bit_test_and_set(&active_count,lock_flag_bit); 68 } 69 lockboost::detail::basic_timed_mutex70 void lock() 71 { 72 if(try_lock()) 73 { 74 return; 75 } 76 long old_count=active_count; 77 mark_waiting_and_try_lock(old_count); 78 79 if(old_count&lock_flag_value) 80 { 81 void* const sem=get_event(); 82 83 do 84 { 85 if(winapi::WaitForSingleObjectEx(sem,::boost::detail::win32::infinite,0)==0) 86 { 87 clear_waiting_and_try_lock(old_count); 88 } 89 } 90 while(old_count&lock_flag_value); 91 } 92 } 93 94 // Loop until the number of waiters has been incremented or we've taken the lock flag 95 // The loop is necessary since this function may be called by multiple threads simultaneously mark_waiting_and_try_lockboost::detail::basic_timed_mutex96 void mark_waiting_and_try_lock(long& old_count) 97 { 98 for(;;) 99 { 100 bool const was_locked=(old_count&lock_flag_value) ? true : false; 101 long const new_count=was_locked?(old_count+1):(old_count|lock_flag_value); 102 long const current=BOOST_INTERLOCKED_COMPARE_EXCHANGE(&active_count,new_count,old_count); 103 if(current==old_count) 104 { 105 if(was_locked) 106 old_count=new_count; 107 // else we've taken the lock flag 108 // don't update old_count so that the calling function can see that 109 // the old lock flag was 0 and know that we've taken the lock flag 110 break; 111 } 112 old_count=current; 113 } 114 } 115 116 // Loop until someone else has taken the lock flag and cleared the event set flag or 117 // until we've taken the lock flag and cleared the event set flag and decremented the 118 // number of waiters 119 // The loop is necessary since this function may be called by multiple threads simultaneously clear_waiting_and_try_lockboost::detail::basic_timed_mutex120 void clear_waiting_and_try_lock(long& old_count) 121 { 122 old_count&=~lock_flag_value; 123 old_count|=event_set_flag_value; 124 for(;;) 125 { 126 long const new_count=((old_count&lock_flag_value)?old_count:((old_count-1)|lock_flag_value))&~event_set_flag_value; 127 long const current=BOOST_INTERLOCKED_COMPARE_EXCHANGE(&active_count,new_count,old_count); 128 if(current==old_count) 129 { 130 // if someone else has taken the lock flag 131 // no need to update old_count since old_count == new_count (ignoring 132 // event_set_flag_value which the calling function doesn't care about) 133 // else we've taken the lock flag 134 // don't update old_count so that the calling function can see that 135 // the old lock flag was 0 and know that we've taken the lock flag 136 break; 137 } 138 old_count=current; 139 } 140 } 141 142 private: getMsboost::detail::basic_timed_mutex143 unsigned long getMs(detail::platform_duration const& d) 144 { 145 return static_cast<unsigned long>(d.getMs()); 146 } 147 148 template <typename Duration> getMsboost::detail::basic_timed_mutex149 unsigned long getMs(Duration const& d) 150 { 151 return static_cast<unsigned long>(chrono::ceil<chrono::milliseconds>(d).count()); 152 } 153 154 template <typename Clock, typename Timepoint, typename Duration> do_lock_untilboost::detail::basic_timed_mutex155 bool do_lock_until(Timepoint const& t, Duration const& max) 156 { 157 if(try_lock()) 158 { 159 return true; 160 } 161 162 long old_count=active_count; 163 mark_waiting_and_try_lock(old_count); 164 165 if(old_count&lock_flag_value) 166 { 167 void* const sem=get_event(); 168 169 // If the clock is the system clock, it may jump while this function 170 // is waiting. To compensate for this and time out near the correct 171 // time, we call WaitForSingleObjectEx() in a loop with a short 172 // timeout and recheck the time remaining each time through the loop. 173 do 174 { 175 Duration d(t - Clock::now()); 176 if(d <= Duration::zero()) // timeout occurred 177 { 178 BOOST_INTERLOCKED_DECREMENT(&active_count); 179 return false; 180 } 181 if(max != Duration::zero()) 182 { 183 d = (std::min)(d, max); 184 } 185 if(winapi::WaitForSingleObjectEx(sem,getMs(d),0)==0) 186 { 187 clear_waiting_and_try_lock(old_count); 188 } 189 } 190 while(old_count&lock_flag_value); 191 } 192 return true; 193 } 194 public: 195 196 #if defined BOOST_THREAD_USES_DATETIME timed_lockboost::detail::basic_timed_mutex197 bool timed_lock(::boost::system_time const& wait_until) 198 { 199 const detail::real_platform_timepoint t(wait_until); 200 return do_lock_until<detail::real_platform_clock>(t, detail::platform_milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS)); 201 } 202 203 template<typename Duration> timed_lockboost::detail::basic_timed_mutex204 bool timed_lock(Duration const& timeout) 205 { 206 const detail::mono_platform_timepoint t(detail::mono_platform_clock::now() + detail::platform_duration(timeout)); 207 // The reference clock is steady and so no need to poll periodically, thus 0 ms max (i.e. no max) 208 return do_lock_until<detail::mono_platform_clock>(t, detail::platform_duration::zero()); 209 } 210 timed_lockboost::detail::basic_timed_mutex211 bool timed_lock(boost::xtime const& timeout) 212 { 213 return timed_lock(boost::system_time(timeout)); 214 } 215 #endif 216 #ifdef BOOST_THREAD_USES_CHRONO 217 template <class Rep, class Period> try_lock_forboost::detail::basic_timed_mutex218 bool try_lock_for(const chrono::duration<Rep, Period>& rel_time) 219 { 220 const chrono::steady_clock::time_point t(chrono::steady_clock::now() + rel_time); 221 typedef typename chrono::duration<Rep, Period> Duration; 222 typedef typename common_type<Duration, typename chrono::steady_clock::duration>::type common_duration; 223 // The reference clock is steady and so no need to poll periodically, thus 0 ms max (i.e. no max) 224 return do_lock_until<chrono::steady_clock>(t, common_duration::zero()); 225 } 226 template <class Duration> try_lock_untilboost::detail::basic_timed_mutex227 bool try_lock_until(const chrono::time_point<chrono::steady_clock, Duration>& t) 228 { 229 typedef typename common_type<Duration, typename chrono::steady_clock::duration>::type common_duration; 230 // The reference clock is steady and so no need to poll periodically, thus 0 ms max (i.e. no max) 231 return do_lock_until<chrono::steady_clock>(t, common_duration::zero()); 232 } 233 template <class Clock, class Duration> try_lock_untilboost::detail::basic_timed_mutex234 bool try_lock_until(const chrono::time_point<Clock, Duration>& t) 235 { 236 typedef typename common_type<Duration, typename Clock::duration>::type common_duration; 237 return do_lock_until<Clock>(t, common_duration(chrono::milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS))); 238 } 239 #endif 240 unlockboost::detail::basic_timed_mutex241 void unlock() 242 { 243 // Clear the lock flag using atomic addition (works since long is always 32 bits on Windows) 244 long const old_count=BOOST_INTERLOCKED_EXCHANGE_ADD(&active_count,lock_flag_value); 245 // If someone is waiting to take the lock, set the event set flag and, if 246 // the event set flag hadn't already been set, send an event. 247 if(!(old_count&event_set_flag_value) && (old_count>lock_flag_value)) 248 { 249 if(!win32::interlocked_bit_test_and_set(&active_count,event_set_flag_bit)) 250 { 251 winapi::SetEvent(get_event()); 252 } 253 } 254 } 255 256 private: 257 // Create an event in a thread-safe way 258 // The first thread to create the event wins and all other thread will use that event get_eventboost::detail::basic_timed_mutex259 void* get_event() 260 { 261 void* current_event=::boost::detail::interlocked_read_acquire(&event); 262 263 if(!current_event) 264 { 265 void* const new_event=win32::create_anonymous_event(win32::auto_reset_event,win32::event_initially_reset); 266 #ifdef BOOST_MSVC 267 #pragma warning(push) 268 #pragma warning(disable:4311) 269 #pragma warning(disable:4312) 270 #endif 271 void* const old_event=BOOST_INTERLOCKED_COMPARE_EXCHANGE_POINTER(&event,new_event,0); 272 #ifdef BOOST_MSVC 273 #pragma warning(pop) 274 #endif 275 if(old_event!=0) 276 { 277 winapi::CloseHandle(new_event); 278 return old_event; 279 } 280 else 281 { 282 return new_event; 283 } 284 } 285 return current_event; 286 } 287 288 }; 289 290 } 291 } 292 293 #define BOOST_BASIC_TIMED_MUTEX_INITIALIZER {0} 294 295 #include <boost/config/abi_suffix.hpp> 296 297 #endif 298