]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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/config/abi_prefix.hpp> | |
26 | ||
27 | namespace boost | |
28 | { | |
29 | namespace detail | |
30 | { | |
31 | struct basic_timed_mutex | |
32 | { | |
33 | BOOST_STATIC_CONSTANT(unsigned char,lock_flag_bit=31); | |
34 | BOOST_STATIC_CONSTANT(unsigned char,event_set_flag_bit=30); | |
35 | BOOST_STATIC_CONSTANT(long,lock_flag_value=1<<lock_flag_bit); | |
36 | BOOST_STATIC_CONSTANT(long,event_set_flag_value=1<<event_set_flag_bit); | |
37 | long active_count; | |
38 | void* event; | |
39 | ||
40 | void initialize() | |
41 | { | |
42 | active_count=0; | |
43 | event=0; | |
44 | } | |
45 | ||
46 | void destroy() | |
47 | { | |
48 | #ifdef BOOST_MSVC | |
49 | #pragma warning(push) | |
50 | #pragma warning(disable:4312) | |
51 | #endif | |
52 | void* const old_event=BOOST_INTERLOCKED_EXCHANGE_POINTER(&event,0); | |
53 | #ifdef BOOST_MSVC | |
54 | #pragma warning(pop) | |
55 | #endif | |
56 | if(old_event) | |
57 | { | |
58 | win32::CloseHandle(old_event); | |
59 | } | |
60 | } | |
61 | ||
62 | ||
63 | bool try_lock() BOOST_NOEXCEPT | |
64 | { | |
65 | return !win32::interlocked_bit_test_and_set(&active_count,lock_flag_bit); | |
66 | } | |
67 | ||
68 | void lock() | |
69 | { | |
70 | if(try_lock()) | |
71 | { | |
72 | return; | |
73 | } | |
74 | long old_count=active_count; | |
75 | mark_waiting_and_try_lock(old_count); | |
76 | ||
77 | if(old_count&lock_flag_value) | |
78 | { | |
79 | bool lock_acquired=false; | |
80 | void* const sem=get_event(); | |
81 | ||
82 | do | |
83 | { | |
84 | unsigned const retval(win32::WaitForSingleObjectEx(sem, ::boost::detail::win32::infinite,0)); | |
85 | BOOST_VERIFY(0 == retval || ::boost::detail::win32::wait_abandoned == retval); | |
86 | // BOOST_VERIFY(win32::WaitForSingleObject( | |
87 | // sem,::boost::detail::win32::infinite)==0); | |
88 | clear_waiting_and_try_lock(old_count); | |
89 | lock_acquired=!(old_count&lock_flag_value); | |
90 | } | |
91 | while(!lock_acquired); | |
92 | } | |
93 | } | |
94 | void mark_waiting_and_try_lock(long& old_count) | |
95 | { | |
96 | for(;;) | |
97 | { | |
98 | bool const was_locked=(old_count&lock_flag_value) ? true : false; | |
99 | long const new_count=was_locked?(old_count+1):(old_count|lock_flag_value); | |
100 | long const current=BOOST_INTERLOCKED_COMPARE_EXCHANGE(&active_count,new_count,old_count); | |
101 | if(current==old_count) | |
102 | { | |
103 | if(was_locked) | |
104 | old_count=new_count; | |
105 | break; | |
106 | } | |
107 | old_count=current; | |
108 | } | |
109 | } | |
110 | ||
111 | void clear_waiting_and_try_lock(long& old_count) | |
112 | { | |
113 | old_count&=~lock_flag_value; | |
114 | old_count|=event_set_flag_value; | |
115 | for(;;) | |
116 | { | |
117 | long const new_count=((old_count&lock_flag_value)?old_count:((old_count-1)|lock_flag_value))&~event_set_flag_value; | |
118 | long const current=BOOST_INTERLOCKED_COMPARE_EXCHANGE(&active_count,new_count,old_count); | |
119 | if(current==old_count) | |
120 | { | |
121 | break; | |
122 | } | |
123 | old_count=current; | |
124 | } | |
125 | } | |
126 | ||
127 | ||
128 | #if defined BOOST_THREAD_USES_DATETIME | |
129 | bool timed_lock(::boost::system_time const& wait_until) | |
130 | { | |
131 | if(try_lock()) | |
132 | { | |
133 | return true; | |
134 | } | |
135 | long old_count=active_count; | |
136 | mark_waiting_and_try_lock(old_count); | |
137 | ||
138 | if(old_count&lock_flag_value) | |
139 | { | |
140 | bool lock_acquired=false; | |
141 | void* const sem=get_event(); | |
142 | ||
143 | do | |
144 | { | |
145 | if(win32::WaitForSingleObjectEx(sem,::boost::detail::get_milliseconds_until(wait_until),0)!=0) | |
146 | { | |
147 | BOOST_INTERLOCKED_DECREMENT(&active_count); | |
148 | return false; | |
149 | } | |
150 | clear_waiting_and_try_lock(old_count); | |
151 | lock_acquired=!(old_count&lock_flag_value); | |
152 | } | |
153 | while(!lock_acquired); | |
154 | } | |
155 | return true; | |
156 | } | |
157 | ||
158 | template<typename Duration> | |
159 | bool timed_lock(Duration const& timeout) | |
160 | { | |
161 | return timed_lock(get_system_time()+timeout); | |
162 | } | |
163 | ||
164 | bool timed_lock(boost::xtime const& timeout) | |
165 | { | |
166 | return timed_lock(system_time(timeout)); | |
167 | } | |
168 | #endif | |
169 | #ifdef BOOST_THREAD_USES_CHRONO | |
170 | template <class Rep, class Period> | |
171 | bool try_lock_for(const chrono::duration<Rep, Period>& rel_time) | |
172 | { | |
173 | return try_lock_until(chrono::steady_clock::now() + rel_time); | |
174 | } | |
175 | template <class Clock, class Duration> | |
176 | bool try_lock_until(const chrono::time_point<Clock, Duration>& t) | |
177 | { | |
178 | using namespace chrono; | |
179 | system_clock::time_point s_now = system_clock::now(); | |
180 | typename Clock::time_point c_now = Clock::now(); | |
181 | return try_lock_until(s_now + ceil<system_clock::duration>(t - c_now)); | |
182 | } | |
183 | template <class Duration> | |
184 | bool try_lock_until(const chrono::time_point<chrono::system_clock, Duration>& t) | |
185 | { | |
186 | using namespace chrono; | |
187 | typedef time_point<chrono::system_clock, chrono::system_clock::duration> sys_tmpt; | |
188 | return try_lock_until(sys_tmpt(chrono::ceil<chrono::system_clock::duration>(t.time_since_epoch()))); | |
189 | } | |
190 | bool try_lock_until(const chrono::time_point<chrono::system_clock, chrono::system_clock::duration>& tp) | |
191 | { | |
192 | if(try_lock()) | |
193 | { | |
194 | return true; | |
195 | } | |
196 | long old_count=active_count; | |
197 | mark_waiting_and_try_lock(old_count); | |
198 | ||
199 | if(old_count&lock_flag_value) | |
200 | { | |
201 | bool lock_acquired=false; | |
202 | void* const sem=get_event(); | |
203 | ||
204 | do | |
205 | { | |
206 | chrono::time_point<chrono::system_clock, chrono::system_clock::duration> now = chrono::system_clock::now(); | |
207 | if (tp<=now) { | |
208 | BOOST_INTERLOCKED_DECREMENT(&active_count); | |
209 | return false; | |
210 | } | |
211 | chrono::milliseconds rel_time= chrono::ceil<chrono::milliseconds>(tp-now); | |
212 | ||
213 | if(win32::WaitForSingleObjectEx(sem,static_cast<unsigned long>(rel_time.count()),0)!=0) | |
214 | { | |
215 | BOOST_INTERLOCKED_DECREMENT(&active_count); | |
216 | return false; | |
217 | } | |
218 | clear_waiting_and_try_lock(old_count); | |
219 | lock_acquired=!(old_count&lock_flag_value); | |
220 | } | |
221 | while(!lock_acquired); | |
222 | } | |
223 | return true; | |
224 | } | |
225 | #endif | |
226 | ||
227 | void unlock() | |
228 | { | |
229 | long const offset=lock_flag_value; | |
230 | long const old_count=BOOST_INTERLOCKED_EXCHANGE_ADD(&active_count,lock_flag_value); | |
231 | if(!(old_count&event_set_flag_value) && (old_count>offset)) | |
232 | { | |
233 | if(!win32::interlocked_bit_test_and_set(&active_count,event_set_flag_bit)) | |
234 | { | |
235 | win32::SetEvent(get_event()); | |
236 | } | |
237 | } | |
238 | } | |
239 | ||
240 | private: | |
241 | void* get_event() | |
242 | { | |
243 | void* current_event=::boost::detail::interlocked_read_acquire(&event); | |
244 | ||
245 | if(!current_event) | |
246 | { | |
247 | void* const new_event=win32::create_anonymous_event(win32::auto_reset_event,win32::event_initially_reset); | |
248 | #ifdef BOOST_MSVC | |
249 | #pragma warning(push) | |
250 | #pragma warning(disable:4311) | |
251 | #pragma warning(disable:4312) | |
252 | #endif | |
253 | void* const old_event=BOOST_INTERLOCKED_COMPARE_EXCHANGE_POINTER(&event,new_event,0); | |
254 | #ifdef BOOST_MSVC | |
255 | #pragma warning(pop) | |
256 | #endif | |
257 | if(old_event!=0) | |
258 | { | |
259 | win32::CloseHandle(new_event); | |
260 | return old_event; | |
261 | } | |
262 | else | |
263 | { | |
264 | return new_event; | |
265 | } | |
266 | } | |
267 | return current_event; | |
268 | } | |
269 | ||
270 | }; | |
271 | ||
272 | } | |
273 | } | |
274 | ||
275 | #define BOOST_BASIC_TIMED_MUTEX_INITIALIZER {0} | |
276 | ||
277 | #include <boost/config/abi_suffix.hpp> | |
278 | ||
279 | #endif |