Dobby  3.0
Dobby “Docker based Thingy” is a tool for managing and running OCI containers using crun
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 
40 namespace 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 {
68 public:
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 
98 public:
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 
111 private:
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 
146 public:
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 
249 public:
250  typedef pthread_cond_t* native_handle_type;
251  native_handle_type native_handle()
252  {
253  return &mCond;
254  }
255 
256 private:
257  pthread_cond_t mCond;
258 };
259 
260 } // namespace AICommon
261 
262 
263 #endif // !defined(CONDITIONVARIABLE_H)
264 
Definition: ConditionVariable.h:67