Photon microGUI widgets library 0.6.0
condition_variable.hpp
1#ifndef _STDEX_CONDITION_VARIABLE_H
2#define _STDEX_CONDITION_VARIABLE_H
3
4#if _MSC_VER > 1000
5#pragma once
6#endif // _MSC_VER > 1000
7
8// stdex includes
9#include "./chrono.hpp"
10#include "./system_error.hpp"
11
12// POSIX includes
13#include <pthread.h>
14
15// std includes
16#include <memory>
17#include <limits>
18
19#if defined(_DEBUG) || defined(DEBUG)
20 #include <iostream> // for logging
21#endif
22
23#ifdef _STDEX_NATIVE_CPP11_SUPPORT
24
25#define _STDEX_DELETED_FUNCTION =delete
26#define _STDEX_NOEXCEPT_FUNCTION noexcept
27#define _STDEX_NOEXCEPT(args) noexcept(args)
28
29#else
30
31#define _STDEX_DELETED_FUNCTION
32#define _STDEX_NOEXCEPT_FUNCTION throw()
33#define _STDEX_NOEXCEPT(args)
34
35#endif
36
37// <mutex.hpp> forward declarations
38namespace stdex
39{
40 class mutex;
41
42 template<class _Tp>
43 class unique_lock;
44
45 namespace detail
46 {
47 pthread_mutex_t* _lock_mutex_native_handle(const unique_lock<mutex>&);
48 bool _lock_owns_lock(const unique_lock<mutex>&);
49 } // namespace detail
50
51} // namespace stdex
52
53namespace stdex
54{
55 namespace detail
56 {
57 template<class _Tp>
58 static void _throw_system_error(const _Tp &_errc)
59 {
60 throw stdex::system_error(
61 stdex::make_error_code(_errc));
62 }
63 }
64
66 struct cv_status
67 {
68 enum cv_status_t { no_timeout, timeout };
69
70 cv_status(cv_status_t _cvs) :
71 _cvstat(_cvs)
72 {
73
74 }
75
76 operator cv_status_t() const
77 {
78 return _cvstat;
79 }
80
81 private:
82 cv_status_t _cvstat;
83 };
84
85 namespace detail
86 {
87 template<class _Clock, class _Dur, class _WaitUntilClock>
88 struct _sync_unknown_clock
89 {
90 typedef _Clock _Clock_local;
91 typedef _Dur _Dur_local;
92 typedef typename _Clock_local::time_point _Clock_time_point;
93
94 static chrono::time_point<_WaitUntilClock, _Dur_local> sync_clock_tp(const chrono::time_point<_Clock_local, _Dur_local>& _atime, ...)
95 {
96 const _Clock_time_point _c_entry = _Clock_local::now();
97 const typename _WaitUntilClock::time_point _s_entry = _WaitUntilClock::now();
98
99 _Dur_local _delta = (_atime - _c_entry);
100 return (_s_entry + stdex::chrono::duration_cast<typename _WaitUntilClock::duration>(_delta));
101 }
102
103 static const chrono::time_point<_WaitUntilClock, _Dur_local>& sync_clock_tp(const chrono::time_point<_WaitUntilClock, _Dur_local>& _atime, int)
104 {
105 return _atime;
106 }
107 };
108 }
109
110 class condition_variable
111 {
112 public:
113 typedef pthread_cond_t* native_handle_type;
114
115 condition_variable()
116 {
117 int _err =
118 pthread_cond_init(&_condition_handle, NULL);
119
120 if (0 != _err)
121 throw(
122 stdex::system_error(
123 stdex::make_error_code(stdex::errc::errc_t(_err))
124 )
125 );
126 }
127
128 ~condition_variable() _STDEX_NOEXCEPT(false)
129 {
130 int _err =
131 pthread_cond_destroy(&_condition_handle);
132
133 if (0 != _err)
134 detail::_throw_system_error(stdex::errc::errc_t(_err));
135 }
136
137 inline void wait(unique_lock<mutex> &_lock) _STDEX_NOEXCEPT_FUNCTION
138 {
139 int _err =
140 pthread_cond_wait(&_condition_handle, detail::_lock_mutex_native_handle(_lock));
141
142 if (0 != _err)
143 {
144 #if defined(_DEBUG) || defined(DEBUG)
145 std::cerr << stdex::error_code(
146 stdex::make_error_code(stdex::errc::errc_t(_err))
147 ).message() << std::endl;
148 #endif
149 std::terminate();
150 }
151 }
152
153 template<class _Predicate>
154 void wait(unique_lock<mutex>& _lock, _Predicate _p)
155 {
156 while (!_p())
157 wait(_lock);
158 }
159
160 template<class _Clock, class _Duration>
161 cv_status wait_until(unique_lock<mutex> &_lock, const chrono::time_point<_Clock, _Duration> &_atime)
162 {
163 return wait_until_impl(_lock, _atime);
164 }
165
166 template<class _Clock, class _Duration, class _Predicate>
167 bool wait_until(unique_lock<mutex> &_lock, const chrono::time_point<_Clock, _Duration> &_atime, _Predicate _p)
168 {
169 while (!_p())
170 if (wait_until(_lock, _atime) == cv_status::timeout)
171 return _p();
172 return true;
173 }
174
175 template<class _Rep, class _Period>
176 cv_status wait_for(unique_lock<mutex> &_lock, const chrono::duration<_Rep, _Period> &_rtime)
177 {
178 return wait_for_impl(_lock, _rtime);
179 }
180
181 template<class _Rep, class _Period, class _Predicate>
182 bool wait_for(unique_lock<mutex> &_lock, const chrono::duration<_Rep, _Period> &_rtime, _Predicate _p)
183 {
184 typedef chrono::system_clock sync_clock;
185
186 sync_clock::time_point _atime =
187 sync_clock::now() + chrono::duration_cast<sync_clock::duration>(_rtime);
188
189 // exactly the same as wait_until with predicate
190 while (!_p())
191 if (wait_until(_lock, _atime) == cv_status::timeout)
192 return _p();
193 return true;
194 }
195
196 native_handle_type native_handle()
197 {
198 return &_condition_handle;
199 }
200
201 inline void notify_one() _STDEX_NOEXCEPT_FUNCTION
202 {
203 pthread_cond_signal(&_condition_handle);
204 }
205
206 inline void notify_all() _STDEX_NOEXCEPT_FUNCTION
207 {
208 pthread_cond_broadcast(&_condition_handle);
209 }
210
211 private:
212
213 template<class _Clock, class _Dur>
214 cv_status wait_until_impl(unique_lock<mutex> &_lock, const chrono::time_point<_Clock, _Dur> &_atime)
215 {
216 if (!detail::_lock_owns_lock(_lock))
217 std::terminate();
218
219 typedef chrono::system_clock wait_until_clock;
220
221 // DR 887 - Sync unknown clock to known clock.
222
223
224 wait_until_clock::time_point _tp = chrono::time_point_cast<wait_until_clock::duration>(
225 detail::_sync_unknown_clock<_Clock, _Dur, wait_until_clock>::sync_clock_tp(_atime, 0)
226 );
227
228 if (_tp < wait_until_clock::now())
229 return cv_status::timeout;
230
231 stdex::timespec _tp_as_ts =
232 wait_until_clock::to_timespec(
233 _tp
234 );
235
236 ::timespec _ts;
237
238 _ts.tv_nsec = _tp_as_ts.tv_nsec;
239 _ts.tv_sec = _tp_as_ts.tv_sec;
240
241 int _err =
242 pthread_cond_timedwait(&_condition_handle, detail::_lock_mutex_native_handle(_lock), &_ts);
243
244 #ifdef ETIMEDOUT
245 if(_err && _err != ETIMEDOUT)
246 {
247 #if defined(_DEBUG) || defined(DEBUG)
248 std::cerr << stdex::error_code(
249 stdex::make_error_code(stdex::errc::errc_t(_err))
250 ).message() << std::endl;
251 #endif
252 std::terminate();
253 }
254 #endif
255
256 return (wait_until_clock::now() < _tp
257 ? cv_status::no_timeout : cv_status::timeout);
258 }
259
260 template<class _Rep, class _Period>
261 cv_status wait_for_impl(unique_lock<mutex> &_lock, chrono::duration<_Rep, _Period> _rtime)
262 {
263 if (!detail::_lock_owns_lock(_lock))
264 std::terminate();
265
266 if (_rtime.count() < 0)
267 return cv_status::timeout;
268
269 typedef chrono::system_clock time_measurment_clock;
270 typedef chrono::system_clock wait_for_clock;
271
272 if (ratio_greater<time_measurment_clock::period, _Period>::value)
273 ++_rtime;
274
275 time_measurment_clock::time_point
276 _start_time_point = time_measurment_clock::now(),
277 _end_time_point = _start_time_point + stdex::chrono::duration_cast<time_measurment_clock::duration>(_rtime);
278
279 stdex::timespec _tp_as_ts =
280 wait_for_clock::to_timespec(wait_for_clock::now() + stdex::chrono::duration_cast<wait_for_clock::duration>(_rtime));
281
282 ::timespec _ts;
283 _ts.tv_sec = _tp_as_ts.tv_sec;
284 _ts.tv_nsec = _tp_as_ts.tv_nsec;
285
286 if ((time_measurment_clock::now() - _start_time_point) > _rtime)
287 return cv_status::timeout;
288
289 int _err =
290 pthread_cond_timedwait(&_condition_handle, detail::_lock_mutex_native_handle(_lock), &_ts);
291
292 #ifdef ETIMEDOUT
293 if(_err && _err != ETIMEDOUT)
294 {
295 #if defined(_DEBUG) || defined(DEBUG)
296 std::cerr << stdex::error_code(
297 stdex::make_error_code(stdex::errc::errc_t(_err))
298 ).message() << std::endl;
299 #endif
300 std::terminate();
301 }
302 #endif
303
304 return (time_measurment_clock::now() - _start_time_point < _rtime
305 ? cv_status::no_timeout : cv_status::timeout);
306 }
307
308 pthread_cond_t _condition_handle;
309
310 condition_variable(const condition_variable&) _STDEX_DELETED_FUNCTION;
311 condition_variable& operator=(const condition_variable&) _STDEX_DELETED_FUNCTION;
312 };
313
314 void notify_all_at_thread_exit(condition_variable &cond, unique_lock<mutex> &lk);
315} // namespace stdex
316
317#undef _STDEX_DELETED_FUNCTION
318#undef _STDEX_NOEXCEPT_FUNCTION
319#undef _STDEX_NOEXCEPT
320
321#endif // _STDEX_CONDITION_VARIABLE_H
cv_status
Definition: condition_variable.hpp:67