Ticket #11377: condition_variable_steady.hpp

File condition_variable_steady.hpp, 9.5 KB (added by Dave Bacher <dbacher@…>, 7 years ago)

condition_variable header rewritten to use steady clock instead of system clock

Line 
1#ifndef BOOST_THREAD_CONDITION_VARIABLE_STEADY_PTHREAD_HPP
2#define BOOST_THREAD_CONDITION_VARIABLE_STEADY_PTHREAD_HPP
3
4// Distributed under the Boost Software License, Version 1.0. (See
5// accompanying file LICENSE_1_0.txt or copy at
6// http://www.boost.org/LICENSE_1_0.txt)
7// (C) Copyright 2007-10 Anthony Williams
8// (C) Copyright 2011-2012 Vicente J. Botet Escriba
9// Steady clock modifications by David Bacher
10
11#include <pthread.h>
12#include <boost/thread/pthread/condition_variable.hpp>
13
14namespace boost
15{
16
17 class condition_variable_steady
18 {
19 private:
20#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS
21 pthread_mutex_t internal_mutex;
22#endif
23 pthread_cond_t cond;
24
25 public:
26 //private: // used by boost::thread::try_join_until
27
28 inline bool do_wait_until(
29 unique_lock<mutex>& lock,
30 struct timespec const &timeout);
31
32 bool do_wait_for(
33 unique_lock<mutex>& lock,
34 struct timespec const &timeout)
35 {
36 return do_wait_until(lock, boost::detail::timespec_plus(timeout, boost::detail::timespec_now()));
37 }
38
39 public:
40 BOOST_THREAD_NO_COPYABLE(condition_variable_steady)
41 condition_variable_steady()
42 {
43#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS
44 int const res=pthread_mutex_init(&internal_mutex,NULL);
45 if(res)
46 {
47 boost::throw_exception(thread_resource_error(res, "boost::condition_variable_steady::condition_variable_steady() constructor failed in pthread_mutex_init"));
48 }
49#endif
50
51 pthread_condattr_t attr;
52 int const res2 = pthread_condattr_init(&attr);
53 if(res2)
54 {
55#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS
56 BOOST_VERIFY(!pthread_mutex_destroy(&internal_mutex));
57#endif
58 boost::throw_exception(thread_resource_error(res2, "boost::condition_variable_steady::condition_variable_steady() constructor failed in pthread_condattr_init"));
59 }
60 pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
61 int const res3=pthread_cond_init(&cond,&attr);
62 pthread_condattr_destroy(&attr);
63 if(res3)
64 {
65#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS
66 BOOST_VERIFY(!pthread_mutex_destroy(&internal_mutex));
67#endif
68 boost::throw_exception(thread_resource_error(res3, "boost::condition_variable_steady::condition_variable_steady() constructor failed in pthread_cond_init"));
69 }
70
71 }
72 ~condition_variable_steady()
73 {
74 int ret;
75#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS
76 do {
77 ret = pthread_mutex_destroy(&internal_mutex);
78 } while (ret == EINTR);
79 BOOST_ASSERT(!ret);
80#endif
81 do {
82 ret = pthread_cond_destroy(&cond);
83 } while (ret == EINTR);
84 BOOST_ASSERT(!ret);
85 }
86
87 void wait(unique_lock<mutex>& m);
88
89 template<typename predicate_type>
90 void wait(unique_lock<mutex>& m,predicate_type pred)
91 {
92 while(!pred()) wait(m);
93 }
94
95#ifdef BOOST_THREAD_USES_CHRONO
96
97 template <class Duration>
98 cv_status
99 wait_until(
100 unique_lock<mutex>& lock,
101 const chrono::time_point<chrono::steady_clock, Duration>& t)
102 {
103 using namespace chrono;
104 typedef time_point<steady_clock, nanoseconds> nano_sys_tmpt;
105 wait_until(lock,
106 nano_sys_tmpt(ceil<nanoseconds>(t.time_since_epoch())));
107 return steady_clock::now() < t ? cv_status::no_timeout :
108 cv_status::timeout;
109 }
110
111 template <class Clock, class Duration>
112 cv_status
113 wait_until(
114 unique_lock<mutex>& lock,
115 const chrono::time_point<Clock, Duration>& t)
116 {
117 using namespace chrono;
118 steady_clock::time_point s_now = steady_clock::now();
119 typename Clock::time_point c_now = Clock::now();
120 wait_until(lock, s_now + ceil<nanoseconds>(t - c_now));
121 return Clock::now() < t ? cv_status::no_timeout : cv_status::timeout;
122 }
123
124 template <class Clock, class Duration, class Predicate>
125 bool
126 wait_until(
127 unique_lock<mutex>& lock,
128 const chrono::time_point<Clock, Duration>& t,
129 Predicate pred)
130 {
131 while (!pred())
132 {
133 if (wait_until(lock, t) == cv_status::timeout)
134 return pred();
135 }
136 return true;
137 }
138
139
140 template <class Rep, class Period>
141 cv_status
142 wait_for(
143 unique_lock<mutex>& lock,
144 const chrono::duration<Rep, Period>& d)
145 {
146 using namespace chrono;
147 steady_clock::time_point c_now = steady_clock::now();
148 wait_until(lock, c_now + ceil<nanoseconds>(d));
149 return steady_clock::now() - c_now < d ? cv_status::no_timeout :
150 cv_status::timeout;
151
152 }
153
154
155 template <class Rep, class Period, class Predicate>
156 bool
157 wait_for(
158 unique_lock<mutex>& lock,
159 const chrono::duration<Rep, Period>& d,
160 Predicate pred)
161 {
162 return wait_until(lock, chrono::steady_clock::now() + d, boost::move(pred));
163 }
164#endif
165
166#define BOOST_THREAD_DEFINES_CONDITION_VARIABLE_NATIVE_HANDLE
167 typedef pthread_cond_t* native_handle_type;
168 native_handle_type native_handle()
169 {
170 return &cond;
171 }
172
173 void notify_one() BOOST_NOEXCEPT;
174 void notify_all() BOOST_NOEXCEPT;
175
176#ifdef BOOST_THREAD_USES_CHRONO
177 inline cv_status wait_until(
178 unique_lock<mutex>& lk,
179 chrono::time_point<chrono::steady_clock, chrono::nanoseconds> tp)
180 {
181 using namespace chrono;
182 nanoseconds d = tp.time_since_epoch();
183 timespec ts = boost::detail::to_timespec(d);
184 if (do_wait_until(lk, ts)) return cv_status::no_timeout;
185 else return cv_status::timeout;
186 }
187#endif
188 };
189
190 BOOST_THREAD_DECL void notify_all_at_thread_exit(condition_variable_steady& cond, unique_lock<mutex> lk);
191
192
193 inline void condition_variable_steady::wait(unique_lock<mutex>& m)
194 {
195#if defined BOOST_THREAD_THROW_IF_PRECONDITION_NOT_SATISFIED
196 if(! m.owns_lock())
197 {
198 boost::throw_exception(condition_error(-1, "boost::condition_variable_steady::wait() failed precondition mutex not owned"));
199 }
200#endif
201 int res=0;
202 {
203#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS
204 thread_cv_detail::lock_on_exit<unique_lock<mutex> > guard;
205 detail::interruption_checker check_for_interruption(&internal_mutex,&cond);
206 guard.activate(m);
207 do {
208 res = pthread_cond_wait(&cond,&internal_mutex);
209 } while (res == EINTR);
210#else
211 //boost::pthread::pthread_mutex_scoped_lock check_for_interruption(&internal_mutex);
212 pthread_mutex_t* the_mutex = m.mutex()->native_handle();
213 do {
214 res = pthread_cond_wait(&cond,the_mutex);
215 } while (res == EINTR);
216#endif
217 }
218#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS
219 this_thread::interruption_point();
220#endif
221 if(res)
222 {
223 boost::throw_exception(condition_error(res, "boost::condition_variable_steady::wait failed in pthread_cond_wait"));
224 }
225 }
226
227 inline bool condition_variable_steady::do_wait_until(
228 unique_lock<mutex>& m,
229 struct timespec const &timeout)
230 {
231#if defined BOOST_THREAD_THROW_IF_PRECONDITION_NOT_SATISFIED
232 if (!m.owns_lock())
233 {
234 boost::throw_exception(condition_error(EPERM, "boost::condition_variable_steady::do_wait_until() failed precondition mutex not owned"));
235 }
236#endif
237 thread_cv_detail::lock_on_exit<unique_lock<mutex> > guard;
238 int cond_res;
239 {
240#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS
241 detail::interruption_checker check_for_interruption(&internal_mutex,&cond);
242 guard.activate(m);
243 cond_res=pthread_cond_timedwait(&cond,&internal_mutex,&timeout);
244#else
245 //boost::pthread::pthread_mutex_scoped_lock check_for_interruption(&internal_mutex);
246 pthread_mutex_t* the_mutex = m.mutex()->native_handle();
247 cond_res=pthread_cond_timedwait(&cond,the_mutex,&timeout);
248#endif
249 }
250#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS
251 this_thread::interruption_point();
252#endif
253 if(cond_res==ETIMEDOUT)
254 {
255 return false;
256 }
257 if(cond_res)
258 {
259 boost::throw_exception(condition_error(cond_res, "boost::condition_variable_steady::do_wait_until failed in pthread_cond_timedwait"));
260 }
261 return true;
262 }
263
264 inline void condition_variable_steady::notify_one() BOOST_NOEXCEPT
265 {
266#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS
267 boost::pthread::pthread_mutex_scoped_lock internal_lock(&internal_mutex);
268#endif
269 BOOST_VERIFY(!pthread_cond_signal(&cond));
270 }
271
272 inline void condition_variable_steady::notify_all() BOOST_NOEXCEPT
273 {
274#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS
275 boost::pthread::pthread_mutex_scoped_lock internal_lock(&internal_mutex);
276#endif
277 BOOST_VERIFY(!pthread_cond_broadcast(&cond));
278 }
279
280}
281
282#endif