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 2011-2012 Vicente J. Botet Escriba
8  * (C) Copyright 2013 Andrey Semashev
9  */
10 /*!
11  * \file   detail/condition_variables/condition_variable_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_CONDITION_VARIABLES_CONDITION_VARIABLE_POSIX_HPP_INCLUDED_
18 #define BOOST_SYNC_DETAIL_CONDITION_VARIABLES_CONDITION_VARIABLE_POSIX_HPP_INCLUDED_
19 
20 #include <cstddef>
21 #include <boost/assert.hpp>
22 #include <boost/utility/enable_if.hpp>
23 #include <boost/mpl/and.hpp>
24 #include <boost/sync/detail/config.hpp>
25 #include <boost/sync/locks/unique_lock_fwd.hpp>
26 #include <boost/sync/exceptions/wait_error.hpp>
27 #include <boost/sync/exceptions/resource_error.hpp>
28 #include <boost/sync/traits/is_condition_variable_compatible.hpp>
29 #include <boost/sync/detail/pthread.hpp>
30 #include <boost/sync/detail/time_traits.hpp>
31 #include <boost/sync/detail/time_units.hpp>
32 #include <boost/sync/detail/throw_exception.hpp>
33 #include <boost/sync/condition_variables/cv_status.hpp>
34 #include <boost/sync/detail/header.hpp>
35 
36 #ifdef BOOST_HAS_PRAGMA_ONCE
37 #pragma once
38 #endif
39 
40 #define BOOST_SYNC_DEFINES_CONDITION_VARIABLE_NATIVE_HANDLE
41 
42 namespace boost {
43 
44 namespace sync {
45 
46 BOOST_SYNC_DETAIL_OPEN_ABI_NAMESPACE {
47 
48 class condition_variable
49 {
50 public:
51     typedef pthread_cond_t* native_handle_type;
52 
53 private:
54     pthread_cond_t m_cond;
55 
56 public:
57 #if defined(PTHREAD_COND_INITIALIZER)
58 #if !defined(BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX)
59 #if !defined(BOOST_NO_CXX11_CONSTEXPR)
60 #define BOOST_SYNC_DEFINES_CONDITION_VARIABLE_CONSTEXPR_CONSTRUCTOR
61 #endif
62 
63     BOOST_CONSTEXPR condition_variable() BOOST_NOEXCEPT : m_cond(PTHREAD_COND_INITIALIZER)
64     {
65     }
66 #else
67     condition_variable() BOOST_NOEXCEPT
68     {
69         BOOST_CONSTEXPR_OR_CONST pthread_cond_t temp = PTHREAD_COND_INITIALIZER;
70         m_cond = temp;
71     }
72 #endif
73 #else // defined(PTHREAD_COND_INITIALIZER)
74     condition_variable()
75     {
76         int const res = pthread_cond_init(&m_cond, NULL);
77         if (res)
78             BOOST_SYNC_DETAIL_THROW(resource_error, (res)("condition_variable constructor failed in pthread_cond_init"));
79     }
80 #endif // defined(PTHREAD_COND_INITIALIZER)
81 
82     ~condition_variable()
83     {
84         BOOST_VERIFY(sync::detail::posix::pthread_cond_destroy(&m_cond) == 0);
85     }
86 
87     void notify_one() BOOST_NOEXCEPT
88     {
89         BOOST_VERIFY(::pthread_cond_signal(&m_cond) == 0);
90     }
91 
92     void notify_all() BOOST_NOEXCEPT
93     {
94         BOOST_VERIFY(::pthread_cond_broadcast(&m_cond) == 0);
95     }
96 
97     template< typename Mutex >
98     typename enable_if< is_condition_variable_compatible< Mutex >, void >::type wait(unique_lock< Mutex >& lock)
99     {
100         BOOST_ASSERT(lock.owns_lock());
101         this->priv_wait(lock.mutex()->native_handle());
102     }
103 
104     template< typename Mutex, typename Predicate >
105     typename enable_if< is_condition_variable_compatible< Mutex >, void >::type wait(unique_lock< Mutex >& lock, Predicate pred)
106     {
107         BOOST_ASSERT(lock.owns_lock());
108         pthread_mutex_t* const mtx = lock.mutex()->native_handle();
109         while (!pred())
110             this->priv_wait(mtx);
111     }
112 
113     template< typename Mutex, typename Time >
114     typename enable_if_c< sync::detail::time_traits< Time >::is_specialized && is_condition_variable_compatible< Mutex >::value, bool >::type
115     timed_wait(unique_lock< Mutex >& lock, Time const& t)
116     {
117         BOOST_ASSERT(lock.owns_lock());
118         return priv_timed_wait(lock, sync::detail::time_traits< Time >::to_sync_unit(t)) == sync::cv_status::no_timeout;
119     }
120 
121     template< typename Mutex, typename TimePoint, typename Predicate >
122     typename enable_if< mpl::and_< detail::is_time_tag_of< TimePoint, detail::time_point_tag >, is_condition_variable_compatible< Mutex > >, bool >::type
123     timed_wait(unique_lock< Mutex >& lock, TimePoint const& t, Predicate pred)
124     {
125         BOOST_ASSERT(lock.owns_lock());
126         typedef typename sync::detail::time_traits< TimePoint >::unit_type unit_type;
127         unit_type abs_timeout = sync::detail::time_traits< TimePoint >::to_sync_unit(t);
128         while (!pred())
129         {
130             if (this->priv_timed_wait(lock, abs_timeout) != sync::cv_status::no_timeout)
131                 return pred();
132         }
133         return true;
134     }
135 
136     template< typename Mutex, typename Duration, typename Predicate >
137     typename enable_if< mpl::and_< detail::is_time_tag_of< Duration, detail::time_duration_tag >, is_condition_variable_compatible< Mutex > >, bool >::type
138     timed_wait(unique_lock< Mutex >& lock, Duration const& t, Predicate pred)
139     {
140         BOOST_ASSERT(lock.owns_lock());
141         sync::detail::system_time_point abs_timeout = sync::detail::system_time_point::now() + sync::detail::time_traits< Duration >::to_sync_unit(t);
142         while (!pred())
143         {
144             if (this->priv_timed_wait(lock, abs_timeout) != sync::cv_status::no_timeout)
145                 return pred();
146         }
147         return true;
148     }
149 
150     template< typename Mutex, typename TimePoint >
151     typename enable_if< mpl::and_< detail::is_time_tag_of< TimePoint, detail::time_point_tag >, is_condition_variable_compatible< Mutex > >, sync::cv_status >::type
152     wait_until(unique_lock< Mutex >& lock, TimePoint const& abs_time)
153     {
154         BOOST_ASSERT(lock.owns_lock());
155         return priv_timed_wait(lock, sync::detail::time_traits< TimePoint >::to_sync_unit(abs_time));
156     }
157 
158     template< typename Mutex, typename TimePoint, typename Predicate >
159     typename enable_if< mpl::and_< detail::is_time_tag_of< TimePoint, detail::time_point_tag >, is_condition_variable_compatible< Mutex > >, bool >::type
160     wait_until(unique_lock< Mutex >& lock, TimePoint const& abs_time, Predicate pred)
161     {
162         BOOST_ASSERT(lock.owns_lock());
163         typedef typename sync::detail::time_traits< TimePoint >::unit_type unit_type;
164         unit_type abs_timeout = sync::detail::time_traits< TimePoint >::to_sync_unit(abs_time);
165         while (!pred())
166         {
167             if (this->priv_timed_wait(lock, abs_timeout) != sync::cv_status::no_timeout)
168                 return pred();
169         }
170         return true;
171     }
172 
173     template< typename Mutex, typename Duration >
174     typename enable_if< mpl::and_< detail::is_time_tag_of< Duration, detail::time_duration_tag >, is_condition_variable_compatible< Mutex > >, sync::cv_status >::type
175     wait_for(unique_lock< Mutex >& lock, Duration const& rel_time)
176     {
177         BOOST_ASSERT(lock.owns_lock());
178         return priv_timed_wait(lock, sync::detail::time_traits< Duration >::to_sync_unit(rel_time));
179     }
180 
181     template< typename Mutex, typename Duration, typename Predicate >
182     typename enable_if< mpl::and_< detail::is_time_tag_of< Duration, detail::time_duration_tag >, is_condition_variable_compatible< Mutex > >, bool >::type
183     wait_for(unique_lock< Mutex >& lock, Duration const& rel_time, Predicate pred)
184     {
185         BOOST_ASSERT(lock.owns_lock());
186         sync::detail::system_time_point abs_timeout = sync::detail::system_time_point::now() + sync::detail::time_traits< Duration >::to_sync_unit(rel_time);
187         while (!pred())
188         {
189             if (this->priv_timed_wait(lock, abs_timeout) != sync::cv_status::no_timeout)
190                 return pred();
191         }
192         return true;
193     }
194 
195     native_handle_type native_handle() BOOST_NOEXCEPT
196     {
197         return &m_cond;
198     }
199 
200     BOOST_DELETED_FUNCTION(condition_variable(condition_variable const&))
201     BOOST_DELETED_FUNCTION(condition_variable& operator= (condition_variable const&))
202 
203 private:
204     void priv_wait(pthread_mutex_t* mtx)
205     {
206         int const res = sync::detail::posix::pthread_cond_wait(&m_cond, mtx);
207         if (res != 0)
208             BOOST_SYNC_DETAIL_THROW(wait_error, (res)("condition_variable::wait failed in pthread_cond_wait"));
209     }
210 
211     template< typename Mutex >
212     sync::cv_status priv_timed_wait(unique_lock< Mutex >& lock, sync::detail::system_duration dur)
213     {
214         return priv_timed_wait(lock, sync::detail::system_time_point::now() + dur);
215     }
216 
217     template< typename Mutex >
218     sync::cv_status priv_timed_wait(unique_lock< Mutex >& lock, sync::detail::system_time_point const& t)
219     {
220         int const res = sync::detail::posix::pthread_cond_timedwait(&m_cond, lock.mutex()->native_handle(), &t.get());
221         if (res == ETIMEDOUT)
222             return sync::cv_status::timeout;
223         else if (res != 0)
224             BOOST_SYNC_DETAIL_THROW(wait_error, (res)("condition_variable::timed_wait failed in pthread_cond_timedwait"));
225         return sync::cv_status::no_timeout;
226     }
227 
228     template< typename Mutex, typename TimePoint >
229     sync::cv_status priv_timed_wait(unique_lock< Mutex >& lock, sync::detail::chrono_time_point< TimePoint > const& t)
230     {
231         typedef TimePoint time_point;
232         typedef typename time_point::clock clock;
233         typedef typename time_point::duration duration;
234         time_point now = clock::now();
235         while (now < t.get())
236         {
237             if (priv_timed_wait(lock, sync::detail::time_traits< duration >::to_sync_unit(t.get() - now)) == sync::cv_status::no_timeout)
238                 return sync::cv_status::no_timeout;
239             now = clock::now();
240         }
241         return sync::cv_status::timeout;
242     }
243 };
244 
245 } // namespace posix
246 
247 } // namespace sync
248 
249 } // namespace boost
250 
251 #include <boost/sync/detail/footer.hpp>
252 
253 #endif // BOOST_SYNC_DETAIL_CONDITION_VARIABLES_CONDITION_VARIABLE_POSIX_HPP_INCLUDED_
254