Dobby 3.0
Dobby “Docker based Thingy” is a tool for managing and running OCI containers using crun
Loading...
Searching...
No Matches
ConditionVariable.h
1/*
2* If not stated otherwise in this file or this component's LICENSE file the
3* following copyright and licenses apply:
4*
5* Copyright 2016 Sky UK
6*
7* Licensed under the Apache License, Version 2.0 (the "License");
8* you may not use this file except in compliance with the License.
9* You may obtain a copy of the License at
10*
11* http://www.apache.org/licenses/LICENSE-2.0
12*
13* Unless required by applicable law or agreed to in writing, software
14* distributed under the License is distributed on an "AS IS" BASIS,
15* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16* See the License for the specific language governing permissions and
17* limitations under the License.
18*/
19/*
20 * File: ConditionVariable.h
21 *
22 */
23#ifndef CONDITIONVARIABLE_H
24#define CONDITIONVARIABLE_H
25
26#include "Mutex.h"
27
28#include <pthread.h>
29#include <time.h>
30
31#include <chrono>
32#include <mutex>
33#include <condition_variable>
34
35
36#if defined(__APPLE__)
37# define HAS_PTHREAD_COND_TIMEDWAIT_RELATIVE_NP 1
38#endif
39
40namespace AICommon
41{
42
43#if (AI_BUILD_TYPE == AI_RELEASE)
44 #define __ConditionVariableThrowOnError(err) \
45 (void)err
46
47#elif (AI_BUILD_TYPE == AI_DEBUG)
48 #define __ConditionVariableThrowOnError(err) \
49 do { \
50 if (__builtin_expect((err != 0), 0)) \
51 throw std::system_error(std::error_code(err, std::system_category())); \
52 } while(0)
53
54#else
55 #error Unknown AI build type
56
57#endif
58
59
67{
68public:
70 {
71 pthread_condattr_t attr;
72 pthread_condattr_init(&attr);
73 #if !defined(HAS_PTHREAD_COND_TIMEDWAIT_RELATIVE_NP)
74 pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
75 #endif
76
77 pthread_cond_init(&mCond, &attr);
78
79 pthread_condattr_destroy(&attr);
80 }
81
82 ConditionVariable(const ConditionVariable&) = delete;
83 ConditionVariable(const ConditionVariable&&) = delete;
84
86 {
87 int err = pthread_cond_destroy(&mCond);
88 try
89 {
90 __ConditionVariableThrowOnError(err);
91 }
92 catch(const std::system_error& exception)
93 {
94 AI_LOG_FATAL("Condition variable failed to be destroyed %s", exception.what());
95 }
96 }
97
98public:
99 void notify_one()
100 {
101 int err = pthread_cond_signal(&mCond);
102 __ConditionVariableThrowOnError(err);
103 }
104
105 void notify_all()
106 {
107 int err = pthread_cond_broadcast(&mCond);
108 __ConditionVariableThrowOnError(err);
109 }
110
111private:
112 struct timespec calcTimeoutAbs(const std::chrono::nanoseconds& rel_time)
113 {
114 struct timespec ts;
115 clock_gettime(CLOCK_MONOTONIC, &ts);
116
117 ts.tv_sec += std::chrono::duration_cast<std::chrono::seconds>(rel_time).count();
118 ts.tv_nsec += (rel_time % std::chrono::seconds(1)).count();
119
120 if (ts.tv_nsec >= 1000000000L)
121 {
122 ts.tv_nsec -= 1000000000L;
123 ts.tv_sec += 1;
124 }
125 else if (ts.tv_nsec < 0L)
126 {
127 // This can happend when rel_time.tv_nsec is big negative, and ts.tv_nsec
128 // is small positive
129 ts.tv_nsec += 1000000000L;
130 ts.tv_sec -= 1;
131 }
132
133 return ts;
134 }
135
136 struct timespec calcTimeoutRel(const std::chrono::nanoseconds& rel_time)
137 {
138 struct timespec ts;
139
140 ts.tv_sec = std::chrono::duration_cast<std::chrono::seconds>(rel_time).count();
141 ts.tv_nsec = (rel_time % std::chrono::seconds(1)).count();
142
143 return ts;
144 }
145
146public:
147 void wait(std::unique_lock<AICommon::Mutex>& lock)
148 {
149 int err = pthread_cond_wait(&mCond, lock.mutex()->native_handle());
150 __ConditionVariableThrowOnError(err);
151 }
152
153 template<class Predicate>
154 void wait(std::unique_lock<AICommon::Mutex>& lock, Predicate pred)
155 {
156 while (!pred())
157 {
158 int err = pthread_cond_wait(&mCond, lock.mutex()->native_handle());
159 __ConditionVariableThrowOnError(err);
160 }
161 }
162
163
164 template<class Rep, class Period>
165 std::cv_status wait_for(std::unique_lock<AICommon::Mutex>& lock,
166 const std::chrono::duration<Rep, Period>& rel_time)
167 {
168 if (rel_time.count() < 0)
169 {
170 AI_LOG_DEBUG("Negative wait period, timeout occured");
171 return std::cv_status::timeout;
172 }
173 #if defined(HAS_PTHREAD_COND_TIMEDWAIT_RELATIVE_NP)
174 const struct timespec ts = calcTimeoutRel(std::chrono::duration_cast<std::chrono::nanoseconds>(rel_time));
175 int err = pthread_cond_timedwait_relative_np(&mCond, lock.mutex()->native_handle(), &ts);
176 #else
177 const struct timespec ts = calcTimeoutAbs(std::chrono::duration_cast<std::chrono::nanoseconds>(rel_time));
178 int err = pthread_cond_timedwait(&mCond, lock.mutex()->native_handle(), &ts);
179 #endif
180 if (err == 0)
181 {
182 return std::cv_status::no_timeout;
183 }
184 if (err == ETIMEDOUT)
185 {
186 return std::cv_status::timeout;
187 }
188 else
189 {
190 __ConditionVariableThrowOnError(err);
191 return std::cv_status::timeout;
192 }
193 }
194
195 template<class Rep, class Period, class Predicate>
196 bool wait_for(std::unique_lock<AICommon::Mutex>& lock,
197 const std::chrono::duration<Rep, Period>& rel_time,
198 Predicate pred)
199 {
200 if (rel_time.count() < 0)
201 {
202 AI_LOG_DEBUG("Negative wait period, timeout occured");
203 return pred();
204 }
205 #if defined(HAS_PTHREAD_COND_TIMEDWAIT_RELATIVE_NP)
206 const struct timespec ts = calcTimeoutRel(std::chrono::duration_cast<std::chrono::nanoseconds>(rel_time));
207 #else
208 const struct timespec ts = calcTimeoutAbs(std::chrono::duration_cast<std::chrono::nanoseconds>(rel_time));
209 #endif
210
211 while (!pred())
212 {
213 #if defined(HAS_PTHREAD_COND_TIMEDWAIT_RELATIVE_NP)
214 int err = pthread_cond_timedwait_relative_np(&mCond, lock.mutex()->native_handle(), &ts);
215 #else
216 int err = pthread_cond_timedwait(&mCond, lock.mutex()->native_handle(), &ts);
217 #endif
218 if (err == ETIMEDOUT)
219 {
220 return pred();
221 }
222 else if (err != 0)
223 {
224 AI_LOG_FATAL("Condition variable error in wait_for '%d'", err);
225 __ConditionVariableThrowOnError(err);
226 }
227 }
228
229 return true;
230 }
231
232
233 std::cv_status wait_until(std::unique_lock<AICommon::Mutex>& lock,
234 const std::chrono::time_point<std::chrono::steady_clock>& timeout_time)
235 {
236 auto rel_time = (timeout_time - std::chrono::steady_clock::now());
237 return wait_for(lock, rel_time);
238 }
239
240 template<class Predicate>
241 bool wait_until(std::unique_lock<AICommon::Mutex>& lock,
242 const std::chrono::time_point<std::chrono::steady_clock>& timeout_time,
243 Predicate pred)
244 {
245 auto rel_time = (timeout_time - std::chrono::steady_clock::now());
246 return wait_for(lock, rel_time, pred);
247 }
248
249public:
250 typedef pthread_cond_t* native_handle_type;
251 native_handle_type native_handle()
252 {
253 return &mCond;
254 }
255
256private:
257 pthread_cond_t mCond;
258};
259
260} // namespace AICommon
261
262
263#endif // !defined(CONDITIONVARIABLE_H)
264
Definition ConditionVariable.h:67