1 #ifndef BOOST_THREAD_PTHREAD_MUTEX_HPP
2 #define BOOST_THREAD_PTHREAD_MUTEX_HPP
3 // (C) Copyright 2007-8 Anthony Williams
4 // (C) Copyright 2011,2012,2015 Vicente J. Botet Escriba
5 // Distributed under the Boost Software License, Version 1.0. (See
6 // accompanying file LICENSE_1_0.txt or copy at
7 // http://www.boost.org/LICENSE_1_0.txt)
8
9 #include <boost/thread/detail/config.hpp>
10 #include <boost/assert.hpp>
11 #include <pthread.h>
12 #include <boost/throw_exception.hpp>
13 #include <boost/core/ignore_unused.hpp>
14 #include <boost/thread/exceptions.hpp>
15 #if defined BOOST_THREAD_PROVIDES_NESTED_LOCKS
16 #include <boost/thread/lock_types.hpp>
17 #endif
18 #include <boost/thread/thread_time.hpp>
19 #if defined BOOST_THREAD_USES_DATETIME
20 #include <boost/thread/xtime.hpp>
21 #endif
22 #include <boost/assert.hpp>
23 #include <errno.h>
24 #include <boost/thread/detail/platform_time.hpp>
25 #include <boost/thread/pthread/pthread_mutex_scoped_lock.hpp>
26 #include <boost/thread/pthread/pthread_helpers.hpp>
27 #ifdef BOOST_THREAD_USES_CHRONO
28 #include <boost/chrono/system_clocks.hpp>
29 #include <boost/chrono/ceil.hpp>
30 #endif
31 #include <boost/thread/detail/delete.hpp>
32
33
34 #include <boost/config/abi_prefix.hpp>
35
36 #ifndef BOOST_THREAD_HAS_NO_EINTR_BUG
37 #define BOOST_THREAD_HAS_EINTR_BUG
38 #endif
39
40 namespace boost
41 {
42 namespace posix {
43 #ifdef BOOST_THREAD_HAS_EINTR_BUG
44 BOOST_FORCEINLINE int pthread_mutex_destroy(pthread_mutex_t* m)
45 {
46 int ret;
47 do
48 {
49 ret = ::pthread_mutex_destroy(m);
50 } while (ret == EINTR);
51 return ret;
52 }
53 BOOST_FORCEINLINE int pthread_mutex_lock(pthread_mutex_t* m)
54 {
55 int ret;
56 do
57 {
58 ret = ::pthread_mutex_lock(m);
59 } while (ret == EINTR);
60 return ret;
61 }
62 BOOST_FORCEINLINE int pthread_mutex_unlock(pthread_mutex_t* m)
63 {
64 int ret;
65 do
(3) Event loop_begin: |
Jumped back to beginning of loop. |
66 {
(1) Event unlock: |
"pthread_mutex_unlock" unlocks "m". |
(4) Event double_unlock: |
"pthread_mutex_unlock" unlocks "m" while it is unlocked. |
67 ret = ::pthread_mutex_unlock(m);
(2) Event cond_true: |
Condition "ret == 4", taking true branch. |
68 } while (ret == EINTR);
69 return ret;
70 }
71 #else
72 BOOST_FORCEINLINE int pthread_mutex_destroy(pthread_mutex_t* m)
73 {
74 return ::pthread_mutex_destroy(m);
75 }
76 BOOST_FORCEINLINE int pthread_mutex_lock(pthread_mutex_t* m)
77 {
78 return ::pthread_mutex_lock(m);
79 }
80 BOOST_FORCEINLINE int pthread_mutex_unlock(pthread_mutex_t* m)
81 {
82 return ::pthread_mutex_unlock(m);
83 }
84
85 #endif
86
87 }
88 class mutex
89 {
90 private:
91 pthread_mutex_t m;
92 public:
93 BOOST_THREAD_NO_COPYABLE(mutex)
94
95 mutex()
96 {
97 int const res=pthread_mutex_init(&m,NULL);
98 if(res)
99 {
100 boost::throw_exception(thread_resource_error(res, "boost:: mutex constructor failed in pthread_mutex_init"));
101 }
102 }
103 ~mutex()
104 {
105 int const res = posix::pthread_mutex_destroy(&m);
106 boost::ignore_unused(res);
107 BOOST_ASSERT(!res);
108 }
109
110 void lock()
111 {
112 int res = posix::pthread_mutex_lock(&m);
113 if (res)
114 {
115 boost::throw_exception(lock_error(res,"boost: mutex lock failed in pthread_mutex_lock"));
116 }
117 }
118
119 void unlock()
120 {
121 int res = posix::pthread_mutex_unlock(&m);
122 (void)res;
123 BOOST_ASSERT(res == 0);
124 // if (res)
125 // {
126 // boost::throw_exception(lock_error(res,"boost: mutex unlock failed in pthread_mutex_unlock"));
127 // }
128 }
129
130 bool try_lock()
131 {
132 int res;
133 do
134 {
135 res = pthread_mutex_trylock(&m);
136 } while (res == EINTR);
137 if (res==EBUSY)
138 {
139 return false;
140 }
141
142 return !res;
143 }
144
145 #define BOOST_THREAD_DEFINES_MUTEX_NATIVE_HANDLE
146 typedef pthread_mutex_t* native_handle_type;
147 native_handle_type native_handle()
148 {
149 return &m;
150 }
151
152 #if defined BOOST_THREAD_PROVIDES_NESTED_LOCKS
153 typedef unique_lock<mutex> scoped_lock;
154 typedef detail::try_lock_wrapper<mutex> scoped_try_lock;
155 #endif
156 };
157
158 typedef mutex try_mutex;
159
160 class timed_mutex
161 {
162 private:
163 pthread_mutex_t m;
164 #ifndef BOOST_THREAD_USES_PTHREAD_TIMEDLOCK
165 pthread_cond_t cond;
166 bool is_locked;
167 #endif
168 public:
169 BOOST_THREAD_NO_COPYABLE(timed_mutex)
170 timed_mutex()
171 {
172 int const res=pthread_mutex_init(&m,NULL);
173 if(res)
174 {
175 boost::throw_exception(thread_resource_error(res, "boost:: timed_mutex constructor failed in pthread_mutex_init"));
176 }
177 #ifndef BOOST_THREAD_USES_PTHREAD_TIMEDLOCK
178 int const res2=pthread::cond_init(cond);
179 if(res2)
180 {
181 BOOST_VERIFY(!posix::pthread_mutex_destroy(&m));
182 boost::throw_exception(thread_resource_error(res2, "boost:: timed_mutex constructor failed in pthread::cond_init"));
183 }
184 is_locked=false;
185 #endif
186 }
187 ~timed_mutex()
188 {
189 BOOST_VERIFY(!posix::pthread_mutex_destroy(&m));
190 #ifndef BOOST_THREAD_USES_PTHREAD_TIMEDLOCK
191 BOOST_VERIFY(!pthread_cond_destroy(&cond));
192 #endif
193 }
194
195 #if defined BOOST_THREAD_USES_DATETIME
196 template<typename TimeDuration>
197 bool timed_lock(TimeDuration const & relative_time)
198 {
199 if (relative_time.is_pos_infinity())
200 {
201 lock();
202 return true;
203 }
204 if (relative_time.is_special())
205 {
206 return true;
207 }
208 detail::platform_duration d(relative_time);
209 #if defined(BOOST_THREAD_HAS_MONO_CLOCK) && !defined(BOOST_THREAD_INTERNAL_CLOCK_IS_MONO)
210 const detail::mono_platform_timepoint ts(detail::mono_platform_clock::now() + d);
211 d = (std::min)(d, detail::platform_milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS));
212 while ( ! do_try_lock_until(detail::internal_platform_clock::now() + d) )
213 {
214 d = ts - detail::mono_platform_clock::now();
215 if ( d <= detail::platform_duration::zero() ) return false; // timeout occurred
216 d = (std::min)(d, detail::platform_milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS));
217 }
218 return true;
219 #else
220 return do_try_lock_until(detail::internal_platform_clock::now() + d);
221 #endif
222 }
223 bool timed_lock(boost::xtime const & absolute_time)
224 {
225 return timed_lock(system_time(absolute_time));
226 }
227 #endif
228 #ifdef BOOST_THREAD_USES_PTHREAD_TIMEDLOCK
229 void lock()
230 {
231 int res = posix::pthread_mutex_lock(&m);
232 if (res)
233 {
234 boost::throw_exception(lock_error(res,"boost: mutex lock failed in pthread_mutex_lock"));
235 }
236 }
237
238 void unlock()
239 {
240 int res = posix::pthread_mutex_unlock(&m);
241 (void)res;
242 BOOST_ASSERT(res == 0);
243 // if (res)
244 // {
245 // boost::throw_exception(lock_error(res,"boost: mutex unlock failed in pthread_mutex_unlock"));
246 // }
247 }
248
249 bool try_lock()
250 {
251 int res;
252 do
253 {
254 res = pthread_mutex_trylock(&m);
255 } while (res == EINTR);
256 if (res==EBUSY)
257 {
258 return false;
259 }
260
261 return !res;
262 }
263
264
265 private:
266 bool do_try_lock_until(detail::internal_platform_timepoint const &timeout)
267 {
268 int const res=pthread_mutex_timedlock(&m,&timeout.getTs());
269 BOOST_ASSERT(!res || res==ETIMEDOUT);
270 return !res;
271 }
272 public:
273
274 #else
275 void lock()
276 {
277 boost::pthread::pthread_mutex_scoped_lock const local_lock(&m);
278 while(is_locked)
279 {
280 BOOST_VERIFY(!pthread_cond_wait(&cond,&m));
281 }
282 is_locked=true;
283 }
284
285 void unlock()
286 {
287 boost::pthread::pthread_mutex_scoped_lock const local_lock(&m);
288 is_locked=false;
289 BOOST_VERIFY(!pthread_cond_signal(&cond));
290 }
291
292 bool try_lock()
293 {
294 boost::pthread::pthread_mutex_scoped_lock const local_lock(&m);
295 if(is_locked)
296 {
297 return false;
298 }
299 is_locked=true;
300 return true;
301 }
302
303 private:
304 bool do_try_lock_until(detail::internal_platform_timepoint const &timeout)
305 {
306 boost::pthread::pthread_mutex_scoped_lock const local_lock(&m);
307 while(is_locked)
308 {
309 int const cond_res=pthread_cond_timedwait(&cond,&m,&timeout.getTs());
310 if(cond_res==ETIMEDOUT)
311 {
312 break;
313 }
314 BOOST_ASSERT(!cond_res);
315 }
316 if(is_locked)
317 {
318 return false;
319 }
320 is_locked=true;
321 return true;
322 }
323 public:
324 #endif
325
326 #if defined BOOST_THREAD_USES_DATETIME
327 bool timed_lock(system_time const & abs_time)
328 {
329 const detail::real_platform_timepoint ts(abs_time);
330 #if defined BOOST_THREAD_INTERNAL_CLOCK_IS_MONO
331 detail::platform_duration d(ts - detail::real_platform_clock::now());
332 d = (std::min)(d, detail::platform_milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS));
333 while ( ! do_try_lock_until(detail::internal_platform_clock::now() + d) )
334 {
335 d = ts - detail::real_platform_clock::now();
336 if ( d <= detail::platform_duration::zero() ) return false; // timeout occurred
337 d = (std::min)(d, detail::platform_milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS));
338 }
339 return true;
340 #else
341 return do_try_lock_until(ts);
342 #endif
343 }
344 #endif
345 #ifdef BOOST_THREAD_USES_CHRONO
346 template <class Rep, class Period>
347 bool try_lock_for(const chrono::duration<Rep, Period>& rel_time)
348 {
349 return try_lock_until(chrono::steady_clock::now() + rel_time);
350 }
351 template <class Clock, class Duration>
352 bool try_lock_until(const chrono::time_point<Clock, Duration>& t)
353 {
354 typedef typename common_type<Duration, typename Clock::duration>::type common_duration;
355 common_duration d(t - Clock::now());
356 d = (std::min)(d, common_duration(chrono::milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS)));
357 while ( ! try_lock_until(detail::internal_chrono_clock::now() + d))
358 {
359 d = t - Clock::now();
360 if ( d <= common_duration::zero() ) return false; // timeout occurred
361 d = (std::min)(d, common_duration(chrono::milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS)));
362 }
363 return true;
364 }
365 template <class Duration>
366 bool try_lock_until(const chrono::time_point<detail::internal_chrono_clock, Duration>& t)
367 {
368 detail::internal_platform_timepoint ts(t);
369 return do_try_lock_until(ts);
370 }
371 #endif
372
373 #define BOOST_THREAD_DEFINES_TIMED_MUTEX_NATIVE_HANDLE
374 typedef pthread_mutex_t* native_handle_type;
375 native_handle_type native_handle()
376 {
377 return &m;
378 }
379
380 #if defined BOOST_THREAD_PROVIDES_NESTED_LOCKS
381 typedef unique_lock<timed_mutex> scoped_timed_lock;
382 typedef detail::try_lock_wrapper<timed_mutex> scoped_try_lock;
383 typedef scoped_timed_lock scoped_lock;
384 #endif
385 };
386 }
387
388 #include <boost/config/abi_suffix.hpp>
389
390
391 #endif
392