]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/boost/thread/win32/basic_timed_mutex.hpp
update sources to ceph Nautilus 14.2.1
[ceph.git] / ceph / src / boost / boost / thread / win32 / basic_timed_mutex.hpp
CommitLineData
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
11fdf7f2
TL
25#include <boost/thread/detail/platform_time.hpp>
26
7c673cae
FG
27#include <boost/config/abi_prefix.hpp>
28
29namespace 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
42 void initialize()
43 {
44 active_count=0;
45 event=0;
46 }
47
48 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 {
b32b8144 60 winapi::CloseHandle(old_event);
7c673cae
FG
61 }
62 }
63
11fdf7f2 64 // Take the lock flag if it's available
7c673cae
FG
65 bool try_lock() BOOST_NOEXCEPT
66 {
67 return !win32::interlocked_bit_test_and_set(&active_count,lock_flag_bit);
68 }
69
70 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 {
7c673cae
FG
81 void* const sem=get_event();
82
83 do
84 {
11fdf7f2
TL
85 if(winapi::WaitForSingleObjectEx(sem,::boost::detail::win32::infinite,0)==0)
86 {
87 clear_waiting_and_try_lock(old_count);
88 }
7c673cae 89 }
11fdf7f2 90 while(old_count&lock_flag_value);
7c673cae
FG
91 }
92 }
11fdf7f2
TL
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
7c673cae
FG
96 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;
11fdf7f2
TL
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
7c673cae
FG
110 break;
111 }
112 old_count=current;
113 }
114 }
115
11fdf7f2
TL
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
7c673cae
FG
120 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 {
11fdf7f2
TL
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
7c673cae
FG
136 break;
137 }
138 old_count=current;
139 }
140 }
141
11fdf7f2
TL
142 private:
143 unsigned long getMs(detail::platform_duration const& d)
144 {
145 return static_cast<unsigned long>(d.getMs());
146 }
147
148 template <typename Duration>
149 unsigned long getMs(Duration const& d)
150 {
151 return static_cast<unsigned long>(chrono::ceil<chrono::milliseconds>(d).count());
152 }
7c673cae 153
11fdf7f2
TL
154 template <typename Clock, typename Timepoint, typename Duration>
155 bool do_lock_until(Timepoint const& t, Duration const& max)
7c673cae
FG
156 {
157 if(try_lock())
158 {
159 return true;
160 }
11fdf7f2 161
7c673cae
FG
162 long old_count=active_count;
163 mark_waiting_and_try_lock(old_count);
164
165 if(old_count&lock_flag_value)
166 {
7c673cae
FG
167 void* const sem=get_event();
168
11fdf7f2
TL
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.
7c673cae
FG
173 do
174 {
11fdf7f2
TL
175 Duration d(t - Clock::now());
176 if(d <= Duration::zero()) // timeout occurred
7c673cae
FG
177 {
178 BOOST_INTERLOCKED_DECREMENT(&active_count);
179 return false;
180 }
11fdf7f2
TL
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 }
7c673cae 189 }
11fdf7f2 190 while(old_count&lock_flag_value);
7c673cae
FG
191 }
192 return true;
193 }
11fdf7f2
TL
194 public:
195
196#if defined BOOST_THREAD_USES_DATETIME
197 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 }
7c673cae
FG
202
203 template<typename Duration>
204 bool timed_lock(Duration const& timeout)
205 {
11fdf7f2
TL
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());
7c673cae
FG
209 }
210
211 bool timed_lock(boost::xtime const& timeout)
212 {
11fdf7f2 213 return timed_lock(boost::system_time(timeout));
7c673cae
FG
214 }
215#endif
216#ifdef BOOST_THREAD_USES_CHRONO
217 template <class Rep, class Period>
218 bool try_lock_for(const chrono::duration<Rep, Period>& rel_time)
219 {
11fdf7f2
TL
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>
227 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());
7c673cae
FG
232 }
233 template <class Clock, class Duration>
234 bool try_lock_until(const chrono::time_point<Clock, Duration>& t)
235 {
11fdf7f2
TL
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)));
7c673cae
FG
238 }
239#endif
240
241 void unlock()
242 {
11fdf7f2 243 // Clear the lock flag using atomic addition (works since long is always 32 bits on Windows)
7c673cae 244 long const old_count=BOOST_INTERLOCKED_EXCHANGE_ADD(&active_count,lock_flag_value);
11fdf7f2
TL
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))
7c673cae
FG
248 {
249 if(!win32::interlocked_bit_test_and_set(&active_count,event_set_flag_bit))
250 {
b32b8144 251 winapi::SetEvent(get_event());
7c673cae
FG
252 }
253 }
254 }
255
256 private:
11fdf7f2
TL
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
7c673cae
FG
259 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 {
b32b8144 277 winapi::CloseHandle(new_event);
7c673cae
FG
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