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_posix.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_POSIX_HPP_INCLUDED_
18 #define BOOST_SYNC_DETAIL_MUTEXES_TIMED_MUTEX_POSIX_HPP_INCLUDED_
19 
20 #include <errno.h>
21 #include <cstddef>
22 #include <boost/assert.hpp>
23 #include <boost/utility/enable_if.hpp>
24 #include <boost/sync/exceptions/lock_error.hpp>
25 #include <boost/sync/exceptions/resource_error.hpp>
26 #include <boost/sync/detail/config.hpp>
27 #include <boost/sync/detail/pthread.hpp>
28 #include <boost/sync/detail/time_traits.hpp>
29 #include <boost/sync/detail/time_units.hpp>
30 #include <boost/sync/detail/throw_exception.hpp>
31 #if !defined(BOOST_SYNC_DETAIL_PTHREAD_HAS_TIMEDLOCK)
32 #include <boost/sync/detail/pthread_mutex_locks.hpp>
33 #endif
34 #include <boost/sync/detail/header.hpp>
35 
36 #ifdef BOOST_HAS_PRAGMA_ONCE
37 #pragma once
38 #endif
39 
40 #if defined(BOOST_SYNC_DETAIL_PTHREAD_HAS_TIMEDLOCK)
41 #define BOOST_SYNC_DEFINES_TIMED_MUTEX_NATIVE_HANDLE
42 #endif
43 
44 namespace boost {
45 
46 namespace sync {
47 
48 BOOST_SYNC_DETAIL_OPEN_ABI_NAMESPACE {
49 
50 class timed_mutex
51 {
52 public:
53 #if defined(BOOST_SYNC_DETAIL_PTHREAD_HAS_TIMEDLOCK)
54     typedef void _is_condition_variable_compatible;
55 #endif
56 #if defined(BOOST_SYNC_DEFINES_TIMED_MUTEX_NATIVE_HANDLE)
57     typedef pthread_mutex_t* native_handle_type;
58 #endif
59 
60 private:
61     pthread_mutex_t m_mutex;
62 #if !defined(BOOST_SYNC_DETAIL_PTHREAD_HAS_TIMEDLOCK)
63     pthread_cond_t m_cond;
64     bool m_is_locked;
65 #endif
66 
67 public:
68 #if defined(PTHREAD_MUTEX_INITIALIZER) && (defined(BOOST_SYNC_DETAIL_PTHREAD_HAS_TIMEDLOCK) || defined(PTHREAD_COND_INITIALIZER))
69 #if !defined(BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX)
70 #if !defined(BOOST_NO_CXX11_CONSTEXPR)
71 #define BOOST_SYNC_DEFINES_TIMED_MUTEX_CONSTEXPR_CONSTRUCTOR
72 #endif
73 
74     BOOST_CONSTEXPR timed_mutex() BOOST_NOEXCEPT :
75         m_mutex(PTHREAD_MUTEX_INITIALIZER)
76 #if !defined(BOOST_SYNC_DETAIL_PTHREAD_HAS_TIMEDLOCK)
77         , m_cond(PTHREAD_COND_INITIALIZER),
78         m_is_locked(false)
79 #endif
80     {
81     }
82 #else
83     timed_mutex() BOOST_NOEXCEPT
84     {
85         BOOST_CONSTEXPR_OR_CONST pthread_mutex_t temp = PTHREAD_MUTEX_INITIALIZER;
86         m_mutex = temp;
87 
88 #if !defined(BOOST_SYNC_DETAIL_PTHREAD_HAS_TIMEDLOCK)
89         BOOST_CONSTEXPR_OR_CONST pthread_cond_t temp2 = PTHREAD_COND_INITIALIZER;
90         m_cond = temp2;
91         m_is_locked = false;
92 #endif
93     }
94 #endif
95 #else // defined(PTHREAD_MUTEX_INITIALIZER)
96     timed_mutex()
97     {
98         int const res = pthread_mutex_init(&m_mutex, NULL);
99         if (res)
100             BOOST_SYNC_DETAIL_THROW(resource_error, (res)("timed_mutex constructor failed in pthread_mutex_init"));
101 
102 #if !defined(BOOST_SYNC_DETAIL_PTHREAD_HAS_TIMEDLOCK)
103         int const res2 = pthread_cond_init(&m_cond, NULL);
104         if (res2)
105             BOOST_SYNC_DETAIL_THROW(resource_error, (res2)("timed_mutex constructor failed in pthread_cond_init"));
106         m_is_locked = false;
107 #endif
108     }
109 #endif // defined(PTHREAD_MUTEX_INITIALIZER)
110 
111     ~timed_mutex()
112     {
113         BOOST_VERIFY(sync::detail::posix::pthread_mutex_destroy(&m_mutex) == 0);
114 #if !defined(BOOST_SYNC_DETAIL_PTHREAD_HAS_TIMEDLOCK)
115         BOOST_VERIFY(sync::detail::posix::pthread_cond_destroy(&m_cond) == 0);
116 #endif
117     }
118 
119 #if defined(BOOST_SYNC_DETAIL_PTHREAD_HAS_TIMEDLOCK)
120 
121     void lock()
122     {
123         int const res = sync::detail::posix::pthread_mutex_lock(&m_mutex);
124         if (res)
125             BOOST_SYNC_DETAIL_THROW(lock_error, (res)("timed_mutex lock failed in pthread_mutex_lock"));
126     }
127 
128     void unlock() BOOST_NOEXCEPT
129     {
130         BOOST_VERIFY(sync::detail::posix::pthread_mutex_unlock(&m_mutex) == 0);
131     }
132 
133     bool try_lock()
134     {
135         int const res = sync::detail::posix::pthread_mutex_trylock(&m_mutex);
136 
137         if (res == 0)
138             return true;
139         else if (res != EBUSY)
140             BOOST_SYNC_DETAIL_THROW(lock_error, (res)("timed_mutex try_lock failed in pthread_mutex_trylock"));
141         return false;
142     }
143 
144 #else // defined(BOOST_SYNC_DETAIL_PTHREAD_HAS_TIMEDLOCK)
145 
146     void lock()
147     {
148         sync::detail::posix::pthread_mutex_lock_guard const local_lock(m_mutex);
149         while (m_is_locked)
150         {
151             BOOST_VERIFY(sync::detail::posix::pthread_cond_wait(&m_cond, &m_mutex) == 0);
152         }
153         m_is_locked = true;
154     }
155 
156     void unlock() BOOST_NOEXCEPT
157     {
158         sync::detail::posix::pthread_mutex_lock_guard const local_lock(m_mutex);
159         m_is_locked = false;
160         BOOST_VERIFY(pthread_cond_signal(&m_cond) == 0);
161     }
162 
163     bool try_lock()
164     {
165         sync::detail::posix::pthread_mutex_lock_guard const local_lock(m_mutex);
166         if (m_is_locked)
167             return false;
168         m_is_locked = true;
169         return true;
170     }
171 
172 #endif // defined(BOOST_SYNC_DETAIL_PTHREAD_HAS_TIMEDLOCK)
173 
174     template< typename Time >
175     typename enable_if_c< sync::detail::time_traits< Time >::is_specialized, bool >::type timed_lock(Time const& t)
176     {
177         return priv_timed_lock(sync::detail::time_traits< Time >::to_sync_unit(t));
178     }
179 
180     template< typename Duration >
181     typename detail::enable_if_tag< Duration, detail::time_duration_tag, bool >::type try_lock_for(Duration const& rel_time)
182     {
183         return priv_timed_lock(sync::detail::time_traits< Duration >::to_sync_unit(rel_time));
184     }
185 
186     template< typename TimePoint >
187     typename detail::enable_if_tag< TimePoint, detail::time_point_tag, bool >::type try_lock_until(TimePoint const& abs_time)
188     {
189         return priv_timed_lock(sync::detail::time_traits< TimePoint >::to_sync_unit(abs_time));
190     }
191 
192 #if defined(BOOST_SYNC_DEFINES_TIMED_MUTEX_NATIVE_HANDLE)
193     native_handle_type native_handle() BOOST_NOEXCEPT
194     {
195         return &m_mutex;
196     }
197 #endif
198 
199     BOOST_DELETED_FUNCTION(timed_mutex(timed_mutex const&))
200     BOOST_DELETED_FUNCTION(timed_mutex& operator= (timed_mutex const&))
201 
202 private:
203     bool priv_timed_lock(sync::detail::system_duration dur)
204     {
205         return priv_timed_lock(sync::detail::system_time_point::now() + dur);
206     }
207 
208     bool priv_timed_lock(sync::detail::system_time_point const& t)
209     {
210 #if defined(BOOST_SYNC_DETAIL_PTHREAD_HAS_TIMEDLOCK)
211 
212         int const res = sync::detail::posix::pthread_mutex_timedlock(&m_mutex, &t.get());
213 
214         if (res == 0)
215             return true;
216         else if (res != ETIMEDOUT)
217             BOOST_SYNC_DETAIL_THROW(lock_error, (res)("timed_mutex timed_lock failed in pthread_mutex_timedlock"));
218         return false;
219 
220 #else // defined(BOOST_SYNC_DETAIL_PTHREAD_HAS_TIMEDLOCK)
221 
222         sync::detail::posix::pthread_mutex_lock_guard const local_lock(m_mutex);
223         while (m_is_locked)
224         {
225             int const cond_res = sync::detail::posix::pthread_cond_timedwait(&m_cond, &m_mutex, &t.get());
226             if (cond_res == ETIMEDOUT)
227             {
228                 if (!m_is_locked)
229                     break;
230                 else
231                     return false;
232             }
233             else if (cond_res != 0)
234             {
235                 BOOST_SYNC_DETAIL_THROW(lock_error, (cond_res)("timed_mutex timed_lock failed in pthread_cond_timedwait"));
236             }
237         }
238         m_is_locked = true;
239         return true;
240 
241 #endif // defined(BOOST_SYNC_DETAIL_PTHREAD_HAS_TIMEDLOCK)
242     }
243 
244     template< typename TimePoint >
245     bool priv_timed_lock(sync::detail::chrono_time_point< TimePoint > const& t)
246     {
247         if (try_lock())
248             return true;
249 
250         typedef TimePoint time_point;
251         typedef typename time_point::clock clock;
252         typedef typename time_point::duration duration;
253         time_point now = clock::now();
254         while (now < t.get())
255         {
256             if (priv_timed_lock(sync::detail::time_traits< duration >::to_sync_unit(t.get() - now)))
257                 return true;
258             now = clock::now();
259         }
260         return false;
261     }
262 };
263 
264 } // namespace posix
265 
266 } // namespace sync
267 
268 } // namespace boost
269 
270 #include <boost/sync/detail/footer.hpp>
271 
272 #endif // BOOST_SYNC_DETAIL_MUTEXES_TIMED_MUTEX_POSIX_HPP_INCLUDED_
273