VLink 2.0.0
A high-performance communication middleware
Loading...
Searching...
No Matches
condition_variable.h
Go to the documentation of this file.
1/*
2 * Copyright (C) 2026 by Thun Lu. All rights reserved.
3 * Author: Thun Lu <thun.lu@zohomail.cn>
4 * Repo: https://github.com/thun-res/vlink
5 * _ __ __ _ __
6 * | | / / / / (_) ____ / /__
7 * | | / / / / / / / __ \ / //_/
8 * | |/ / / /___ / / / / / / / ,<
9 * |___/ /_____/ /_/ /_/ /_/ /_/|_|
10 *
11 * Licensed under the Apache License, Version 2.0 (the "License");
12 * you may not use this file except in compliance with the License.
13 * You may obtain a copy of the License at
14 *
15 * http://www.apache.org/licenses/LICENSE-2.0
16 *
17 * Unless required by applicable law or agreed to in writing, software
18 * distributed under the License is distributed on an "AS IS" BASIS,
19 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 * See the License for the specific language governing permissions and
21 * limitations under the License.
22 */
23
24/*
25 * ConditionVariable implementation to fix monotonic_clock.
26 *
27 * There is a problem with std::condition_variable in lower versions of gcc,
28 * which actually uses std::chrono::system_clock instead of std::chrono::steady_clock.
29 * Bug 41861 (DR887) - [DR887][C++0x] <condition_variable> does not use monotonic_clock
30 *
31 * Since new versions of gcc cannot be used, vlink::condition_variable can only be
32 * manually implemented instead of C++11's std::condition_variable.
33 *
34 * This problem is mainly solved by using the POSIX function pthread_condattr_setclock to
35 * set the attribute of the condition variable to CLOCK_MONOTONIC.
36 */
37
38/**
39 * @file condition_variable.h
40 * @brief POSIX monotonic-clock condition variable replacing std::condition_variable.
41 *
42 * @details
43 * On older versions of GCC (and some libc implementations) @c std::condition_variable
44 * internally uses @c CLOCK_REALTIME for timed waits, which means that NTP adjustments
45 * or system-clock changes can cause spurious wakeups or missed timeouts. This header
46 * provides @c vlink::ConditionVariable and @c vlink::ConditionVariableAny as drop-in
47 * replacements that explicitly configure the underlying @c pthread_cond_t to use
48 * @c CLOCK_MONOTONIC via @c pthread_condattr_setclock.
49 *
50 * On non-POSIX platforms (Windows) the types are aliased to their @c std:: counterparts
51 * because that bug does not apply there.
52 *
53 * Type aliases for convenience:
54 * @code
55 * vlink::condition_variable == vlink::ConditionVariable
56 * vlink::condition_variable_any == vlink::ConditionVariableAny
57 * @endcode
58 *
59 * The public API is identical to @c std::condition_variable and
60 * @c std::condition_variable_any respectively, so existing code can
61 * substitute the types without further changes.
62 *
63 * @note
64 * - On POSIX the @c ConditionVariable is backed by a raw @c pthread_cond_t
65 * initialised with @c CLOCK_MONOTONIC. It is not copyable or movable.
66 * - @c ConditionVariableAny uses a shared internal @c ConditionVariable plus
67 * a @c std::mutex, making it compatible with any @c BasicLockable type.
68 * - The @c ceil() / @c ceil_impl() helper functions ensure that duration
69 * conversions always round up, preventing premature timeouts.
70 *
71 * @par Example
72 * @code
73 * std::mutex mtx;
74 * vlink::ConditionVariable cv;
75 * bool ready = false;
76 *
77 * // Consumer thread:
78 * {
79 * std::unique_lock<std::mutex> lock(mtx);
80 * cv.wait_for(lock, std::chrono::milliseconds(200),
81 * [&]{ return ready; });
82 * }
83 *
84 * // Producer thread:
85 * {
86 * std::lock_guard<std::mutex> lock(mtx);
87 * ready = true;
88 * }
89 * cv.notify_one();
90 * @endcode
91 */
92
93#pragma once
94
95#include <condition_variable>
96#include <memory>
97#include <utility>
98
99#ifdef __unix__
100
101#include <pthread.h>
102
103#include <cerrno>
104#include <chrono>
105#include <mutex>
106
107namespace vlink {
108
109/**
110 * @class ConditionVariable
111 * @brief POSIX monotonic-clock condition variable (pthread-backed).
112 *
113 * @details
114 * Drop-in replacement for @c std::condition_variable that uses
115 * @c CLOCK_MONOTONIC for all timed waits, ensuring correctness in the
116 * presence of NTP adjustments.
117 *
118 * The interface mirrors @c std::condition_variable exactly.
119 */
120class ConditionVariable final {
121 public:
122 /**
123 * @brief The underlying native handle type.
124 */
125 using native_handle_type = pthread_cond_t*;
126
127 ConditionVariable(const ConditionVariable&) noexcept = delete;
128
129 ConditionVariable& operator=(const ConditionVariable&) noexcept = delete;
130
131 /**
132 * @brief Constructs and initialises the condition variable with CLOCK_MONOTONIC.
133 */
134 ConditionVariable() noexcept;
135
136 /**
137 * @brief Destroys the condition variable.
138 */
139 ~ConditionVariable() noexcept;
140
141 /**
142 * @brief Wakes one thread waiting on this condition variable.
143 */
144 void notify_one() noexcept;
145
146 /**
147 * @brief Wakes all threads waiting on this condition variable.
148 */
149 void notify_all() noexcept;
150
151 /**
152 * @brief Atomically releases @p lock and waits for a notification.
153 *
154 * @param lock A held @c unique_lock<mutex> that is released during the wait.
155 */
156 void wait(std::unique_lock<std::mutex>& lock) noexcept;
157
158 /**
159 * @brief Waits until @p p returns @c true, using a spurious-wakeup loop.
160 *
161 * @tparam PredicateT A callable returning @c bool with no arguments.
162 * @param lock A held @c unique_lock<mutex>.
163 * @param p Predicate checked after every wakeup.
164 */
165 template <typename PredicateT>
166 void wait(std::unique_lock<std::mutex>& lock, PredicateT p) noexcept;
167
168 /**
169 * @brief Waits until @p atime or notification, using @c steady_clock.
170 *
171 * @tparam DurationT The duration type of the time point.
172 * @param lock A held @c unique_lock<mutex>.
173 * @param atime Absolute time point (steady_clock).
174 * @return @c cv_status::timeout if the deadline was reached, otherwise
175 * @c cv_status::no_timeout.
176 */
177 template <typename DurationT>
178 std::cv_status wait_until(std::unique_lock<std::mutex>& lock,
179 const std::chrono::time_point<std::chrono::steady_clock, DurationT>& atime) noexcept;
180
181 /**
182 * @brief Waits until @p atime or notification, using @c system_clock.
183 *
184 * @details
185 * Internally converts the @c system_clock time point to the @c steady_clock
186 * equivalent to avoid NTP sensitivity.
187 *
188 * @tparam DurationT The duration type of the time point.
189 * @param lock A held @c unique_lock<mutex>.
190 * @param atime Absolute time point (system_clock).
191 * @return @c cv_status::timeout or @c cv_status::no_timeout.
192 */
193 template <typename DurationT>
194 std::cv_status wait_until(std::unique_lock<std::mutex>& lock,
195 const std::chrono::time_point<std::chrono::system_clock, DurationT>& atime) noexcept;
196
197 /**
198 * @brief Waits until @p atime or notification, for any clock type.
199 *
200 * @details
201 * Converts the given clock time point to a @c steady_clock deadline at the
202 * moment of the call to eliminate clock drift during the wait.
203 *
204 * @tparam ClockT The clock type.
205 * @tparam DurationT The duration type of the time point.
206 * @param lock A held @c unique_lock<mutex>.
207 * @param atime Absolute time point in @c ClockT.
208 * @return @c cv_status::timeout or @c cv_status::no_timeout.
209 */
210 template <typename ClockT, typename DurationT>
211 std::cv_status wait_until(std::unique_lock<std::mutex>& lock,
212 const std::chrono::time_point<ClockT, DurationT>& atime) noexcept;
213
214 /**
215 * @brief Waits until @p atime, a notification, or @p p returns @c true.
216 *
217 * @tparam ClockT The clock type.
218 * @tparam DurationT The duration type of the time point.
219 * @tparam PredicateT A callable returning @c bool.
220 * @param lock A held @c unique_lock<mutex>.
221 * @param atime Absolute deadline.
222 * @param p Predicate checked after every wakeup.
223 * @return The final value of @p p when either the deadline passes or the
224 * predicate becomes @c true.
225 */
226 template <typename ClockT, typename DurationT, typename PredicateT>
227 bool wait_until(std::unique_lock<std::mutex>& lock, const std::chrono::time_point<ClockT, DurationT>& atime,
228 PredicateT p) noexcept;
229
230 /**
231 * @brief Waits for at most @p rtime duration or until notified.
232 *
233 * @tparam RepT The representation type of the duration.
234 * @tparam PeriodT The period of the duration.
235 * @param lock A held @c unique_lock<mutex>.
236 * @param rtime Maximum wait duration.
237 * @return @c cv_status::timeout or @c cv_status::no_timeout.
238 */
239 template <typename RepT, typename PeriodT>
240 std::cv_status wait_for(std::unique_lock<std::mutex>& lock,
241 const std::chrono::duration<RepT, PeriodT>& rtime) noexcept;
242
243 /**
244 * @brief Waits for at most @p rtime or until @p p returns @c true.
245 *
246 * @tparam RepT The representation type of the duration.
247 * @tparam PeriodT The period of the duration.
248 * @tparam PredicateT A callable returning @c bool.
249 * @param lock A held @c unique_lock<mutex>.
250 * @param rtime Maximum wait duration.
251 * @param p Predicate checked after every wakeup.
252 * @return The final value of @p p.
253 */
254 template <typename RepT, typename PeriodT, typename PredicateT>
255 bool wait_for(std::unique_lock<std::mutex>& lock, const std::chrono::duration<RepT, PeriodT>& rtime,
256 PredicateT p) noexcept;
257
258 /**
259 * @brief Returns the native @c pthread_cond_t* handle.
260 *
261 * @return Pointer to the internal @c pthread_cond_t.
262 */
263 [[nodiscard]] native_handle_type native_handle() noexcept;
264
265 private:
266 template <typename ToDurT, typename RepT, typename PeriodT>
267 static constexpr ToDurT ceil(const std::chrono::duration<RepT, PeriodT>& d) noexcept;
268
269 template <typename TpT, typename UpT>
270 static constexpr TpT ceil_impl(const TpT& t, const UpT& u) noexcept;
271
272 template <typename DurationT>
273 std::cv_status wait_until_impl(std::unique_lock<std::mutex>& lock,
274 const std::chrono::time_point<std::chrono::steady_clock, DurationT>& atime) noexcept;
275
276 pthread_cond_t cond_{};
277};
278
279/**
280 * @class ConditionVariableAny
281 * @brief POSIX monotonic-clock condition variable compatible with any @c BasicLockable.
282 *
283 * @details
284 * Drop-in replacement for @c std::condition_variable_any that uses
285 * @c CLOCK_MONOTONIC internally. Accepts any lockable type (not just
286 * @c std::unique_lock<std::mutex>).
287 *
288 * Internally uses a shared @c ConditionVariable wrapped in a @c std::mutex
289 * to provide the @c BasicLockable compatibility layer.
290 */
291class ConditionVariableAny final {
292 public:
293 ConditionVariableAny(const ConditionVariableAny&) noexcept = delete;
294
295 ConditionVariableAny& operator=(const ConditionVariableAny&) noexcept = delete;
296
297 /**
298 * @brief Constructs and initialises the condition variable.
299 */
300 ConditionVariableAny() noexcept;
301
302 /**
303 * @brief Destructor.
304 */
305 ~ConditionVariableAny() noexcept;
306
307 /**
308 * @brief Wakes one thread waiting on this condition variable.
309 */
310 void notify_one() noexcept;
311
312 /**
313 * @brief Wakes all threads waiting on this condition variable.
314 */
315 void notify_all() noexcept;
316
317 /**
318 * @brief Atomically releases @p lock and waits for a notification.
319 *
320 * @tparam LockT Any @c BasicLockable type.
321 * @param lock The lock to release during the wait.
322 */
323 template <typename LockT>
324 void wait(LockT& lock) noexcept;
325
326 /**
327 * @brief Waits until @p p returns @c true, using a spurious-wakeup loop.
328 *
329 * @tparam LockT Any @c BasicLockable type.
330 * @tparam PredicateT A callable returning @c bool.
331 * @param lock The lock to release during the wait.
332 * @param p Predicate checked after every wakeup.
333 */
334 template <typename LockT, typename PredicateT>
335 void wait(LockT& lock, PredicateT p) noexcept;
336
337 /**
338 * @brief Waits until @p atime or notification.
339 *
340 * @tparam LockT Any @c BasicLockable type.
341 * @tparam ClockT Clock type.
342 * @tparam DurationT Duration type.
343 * @param lock The lock to release during the wait.
344 * @param atime Absolute deadline.
345 * @return @c cv_status::timeout or @c cv_status::no_timeout.
346 */
347 template <typename LockT, typename ClockT, typename DurationT>
348 std::cv_status wait_until(LockT& lock, const std::chrono::time_point<ClockT, DurationT>& atime) noexcept;
349
350 /**
351 * @brief Waits until @p atime, notification, or @p p returns @c true.
352 *
353 * @tparam LockT Any @c BasicLockable type.
354 * @tparam ClockT Clock type.
355 * @tparam DurationT Duration type.
356 * @tparam PredicateT A callable returning @c bool.
357 * @param lock The lock to release during the wait.
358 * @param atime Absolute deadline.
359 * @param p Predicate.
360 * @return The final value of @p p.
361 */
362 template <typename LockT, typename ClockT, typename DurationT, typename PredicateT>
363 bool wait_until(LockT& lock, const std::chrono::time_point<ClockT, DurationT>& atime, PredicateT p) noexcept;
364
365 /**
366 * @brief Waits for at most @p rtime or until notified.
367 *
368 * @tparam LockT Any @c BasicLockable type.
369 * @tparam RepT Duration representation type.
370 * @tparam PeriodT Duration period.
371 * @param lock The lock to release during the wait.
372 * @param rtime Maximum wait duration.
373 * @return @c cv_status::timeout or @c cv_status::no_timeout.
374 */
375 template <typename LockT, typename RepT, typename PeriodT>
376 std::cv_status wait_for(LockT& lock, const std::chrono::duration<RepT, PeriodT>& rtime) noexcept;
377
378 /**
379 * @brief Waits for at most @p rtime or until @p p returns @c true.
380 *
381 * @tparam LockT Any @c BasicLockable type.
382 * @tparam RepT Duration representation type.
383 * @tparam PeriodT Duration period.
384 * @tparam PredicateT A callable returning @c bool.
385 * @param lock The lock to release during the wait.
386 * @param rtime Maximum wait duration.
387 * @param p Predicate.
388 * @return The final value of @p p.
389 */
390 template <typename LockT, typename RepT, typename PeriodT, typename PredicateT>
391 bool wait_for(LockT& lock, const std::chrono::duration<RepT, PeriodT>& rtime, PredicateT p) noexcept;
392
393 private:
394 template <typename ToDurT, typename RepT, typename PeriodT>
395 static constexpr ToDurT ceil(const std::chrono::duration<RepT, PeriodT>& d) noexcept;
396
397 template <typename TpT, typename UpT>
398 static constexpr TpT ceil_impl(const TpT& t, const UpT& u) noexcept;
399
400 template <typename LockT, typename DurationT>
401 std::cv_status wait_until_impl(LockT& lock,
402 const std::chrono::time_point<std::chrono::steady_clock, DurationT>& atime) noexcept;
403
404 struct SharedState final {
405 std::mutex mtx;
407 };
408
409 std::shared_ptr<SharedState> shared_state_;
410};
411
412////////////////////////////////////////////////////////////////
413/// Details
414////////////////////////////////////////////////////////////////
415
416inline ConditionVariable::ConditionVariable() noexcept {
417 pthread_condattr_t attr;
418
419 pthread_condattr_init(&attr);
420 pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
421 pthread_cond_init(&cond_, &attr);
422 pthread_condattr_destroy(&attr);
423}
424
425inline ConditionVariable::~ConditionVariable() noexcept { pthread_cond_destroy(&cond_); }
426
427inline void ConditionVariable::notify_one() noexcept { pthread_cond_signal(&cond_); }
428
429inline void ConditionVariable::notify_all() noexcept { pthread_cond_broadcast(&cond_); }
430
431inline void ConditionVariable::wait(std::unique_lock<std::mutex>& lock) noexcept {
432 pthread_cond_wait(&cond_, lock.mutex()->native_handle());
433}
434
435template <typename PredicateT>
436inline void ConditionVariable::wait(std::unique_lock<std::mutex>& lock, PredicateT p) noexcept {
437 while (!p()) {
438 wait(lock);
439 }
440}
441
442template <typename DurationT>
443inline std::cv_status ConditionVariable::wait_until(
444 std::unique_lock<std::mutex>& lock,
445 const std::chrono::time_point<std::chrono::steady_clock, DurationT>& atime) noexcept {
446 return wait_until_impl(lock, atime);
447}
448
449template <typename DurationT>
450inline std::cv_status ConditionVariable::wait_until(
451 std::unique_lock<std::mutex>& lock,
452 const std::chrono::time_point<std::chrono::system_clock, DurationT>& atime) noexcept {
453 return wait_until<std::chrono::system_clock, DurationT>(lock, atime);
454}
455
456template <typename ClockT, typename DurationT>
457inline std::cv_status ConditionVariable::wait_until(std::unique_lock<std::mutex>& lock,
458 const std::chrono::time_point<ClockT, DurationT>& atime) noexcept {
459 const typename ClockT::time_point c_entry = ClockT::now();
460 const std::chrono::steady_clock::time_point s_entry = std::chrono::steady_clock::now();
461 const auto delta = atime - c_entry;
462 const auto s_atime = s_entry + ceil<std::chrono::steady_clock::duration>(delta);
463
464 if (wait_until_impl(lock, s_atime) == std::cv_status::no_timeout) {
465 return std::cv_status::no_timeout;
466 }
467
468 if (ClockT::now() < atime) {
469 return std::cv_status::no_timeout;
470 }
471
472 return std::cv_status::timeout;
473}
474
475template <typename ClockT, typename DurationT, typename PredicateT>
476inline bool ConditionVariable::wait_until(std::unique_lock<std::mutex>& lock,
477 const std::chrono::time_point<ClockT, DurationT>& atime,
478 PredicateT p) noexcept {
479 while (!p()) {
480 if (wait_until(lock, atime) == std::cv_status::timeout) {
481 return p();
482 }
483 }
484
485 return true;
486}
487
488template <typename RepT, typename PeriodT>
489inline std::cv_status ConditionVariable::wait_for(std::unique_lock<std::mutex>& lock,
490 const std::chrono::duration<RepT, PeriodT>& rtime) noexcept {
491 return wait_until(lock, std::chrono::steady_clock::now() + ceil<std::chrono::steady_clock::duration>(rtime));
492}
493
494template <typename RepT, typename PeriodT, typename PredicateT>
495inline bool ConditionVariable::wait_for(std::unique_lock<std::mutex>& lock,
496 const std::chrono::duration<RepT, PeriodT>& rtime, PredicateT p) noexcept {
497 return wait_until(lock, std::chrono::steady_clock::now() + ceil<std::chrono::steady_clock::duration>(rtime),
498 std::move(p));
499}
500
501inline ConditionVariable::native_handle_type ConditionVariable::native_handle() noexcept { return &cond_; }
502
503template <typename ToDurT, typename RepT, typename PeriodT>
504inline constexpr ToDurT ConditionVariable::ceil(const std::chrono::duration<RepT, PeriodT>& d) noexcept {
505 return ceil_impl(std::chrono::duration_cast<ToDurT>(d), d);
506}
507
508template <typename TpT, typename UpT>
509inline constexpr TpT ConditionVariable::ceil_impl(const TpT& t, const UpT& u) noexcept {
510 return (t < u) ? (t + TpT{1}) : t;
511}
512
513template <typename DurationT>
514inline std::cv_status ConditionVariable::wait_until_impl(
515 std::unique_lock<std::mutex>& lock,
516 const std::chrono::time_point<std::chrono::steady_clock, DurationT>& atime) noexcept {
517 if (std::chrono::steady_clock::now() >= atime) {
518 return std::cv_status::timeout;
519 }
520
521 auto s = std::chrono::time_point_cast<std::chrono::seconds>(atime);
522 auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(atime - s);
523
524 struct timespec ts = {static_cast<std::time_t>(s.time_since_epoch().count()),
525 static_cast<long>(ns.count())}; // NOLINT(runtime/int, google-runtime-int)
526
527 int ret = pthread_cond_timedwait(&cond_, lock.mutex()->native_handle(), &ts);
528
529 return (ret == ETIMEDOUT) ? std::cv_status::timeout : std::cv_status::no_timeout;
530}
531
532inline ConditionVariableAny::ConditionVariableAny() noexcept : shared_state_(std::make_shared<SharedState>()) {}
533
534inline ConditionVariableAny::~ConditionVariableAny() noexcept = default;
535
536inline void ConditionVariableAny::notify_one() noexcept {
537 std::shared_ptr<SharedState> state = shared_state_;
538 std::lock_guard<std::mutex> lock(state->mtx);
539 state->cv.notify_one();
540}
541
542inline void ConditionVariableAny::notify_all() noexcept {
543 std::shared_ptr<SharedState> state = shared_state_;
544 std::lock_guard<std::mutex> lock(state->mtx);
545 state->cv.notify_all();
546}
547
548template <typename LockT>
549inline void ConditionVariableAny::wait(LockT& lock) noexcept {
550 std::shared_ptr<SharedState> state = shared_state_;
551 std::unique_lock<std::mutex> internal_lock(state->mtx);
552 lock.unlock();
553
554 struct UnlockGuard final {
555 LockT& lock_ref;
556
557 ~UnlockGuard() noexcept {
558 try {
559 lock_ref.lock();
560 } catch (std::exception&) {
561 }
562 }
563 } guard{lock};
564
565 state->cv.wait(internal_lock);
566}
567
568template <typename LockT, typename PredicateT>
569inline void ConditionVariableAny::wait(LockT& lock, PredicateT p) noexcept {
570 while (!p()) {
571 wait(lock);
572 }
573}
574
575template <typename LockT, typename ClockT, typename DurationT>
576inline std::cv_status ConditionVariableAny::wait_until(
577 LockT& lock, const std::chrono::time_point<ClockT, DurationT>& atime) noexcept {
578 if constexpr (std::is_same_v<ClockT, std::chrono::steady_clock>) {
579 return wait_until_impl(lock, atime);
580 } else {
581 const typename ClockT::time_point c_entry = ClockT::now();
582 const std::chrono::steady_clock::time_point s_entry = std::chrono::steady_clock::now();
583 const auto delta = atime - c_entry;
584 const auto s_atime = s_entry + ceil<std::chrono::steady_clock::duration>(delta);
585
586 if (wait_until_impl(lock, s_atime) == std::cv_status::no_timeout) {
587 return std::cv_status::no_timeout;
588 }
589
590 if (ClockT::now() < atime) {
591 return std::cv_status::no_timeout;
592 }
593
594 return std::cv_status::timeout;
595 }
596}
597
598template <typename LockT, typename ClockT, typename DurationT, typename PredicateT>
599inline bool ConditionVariableAny::wait_until(LockT& lock, const std::chrono::time_point<ClockT, DurationT>& atime,
600 PredicateT p) noexcept {
601 while (!p()) {
602 if (wait_until(lock, atime) == std::cv_status::timeout) {
603 return p();
604 }
605 }
606
607 return true;
608}
609
610template <typename LockT, typename RepT, typename PeriodT>
611inline std::cv_status ConditionVariableAny::wait_for(LockT& lock,
612 const std::chrono::duration<RepT, PeriodT>& rtime) noexcept {
613 return wait_until(lock, std::chrono::steady_clock::now() + ceil<std::chrono::steady_clock::duration>(rtime));
614}
615
616template <typename LockT, typename RepT, typename PeriodT, typename PredicateT>
617inline bool ConditionVariableAny::wait_for(LockT& lock, const std::chrono::duration<RepT, PeriodT>& rtime,
618 PredicateT p) noexcept {
619 return wait_until(lock, std::chrono::steady_clock::now() + ceil<std::chrono::steady_clock::duration>(rtime),
620 std::move(p));
621}
622
623template <typename ToDurT, typename RepT, typename PeriodT>
624inline constexpr ToDurT ConditionVariableAny::ceil(const std::chrono::duration<RepT, PeriodT>& d) noexcept {
625 return ceil_impl(std::chrono::duration_cast<ToDurT>(d), d);
626}
627
628template <typename TpT, typename UpT>
629inline constexpr TpT ConditionVariableAny::ceil_impl(const TpT& t, const UpT& u) noexcept {
630 return (t < u) ? (t + TpT{1}) : t;
631}
632
633template <typename LockT, typename DurationT>
634inline std::cv_status ConditionVariableAny::wait_until_impl(
635 LockT& lock, const std::chrono::time_point<std::chrono::steady_clock, DurationT>& atime) noexcept {
636 if (std::chrono::steady_clock::now() >= atime) {
637 return std::cv_status::timeout;
638 }
639
640 std::shared_ptr<SharedState> state = shared_state_;
641 std::unique_lock<std::mutex> internal_lock(state->mtx);
642 lock.unlock();
643
644 struct UnlockGuard final {
645 LockT& lock_ref;
646
647 ~UnlockGuard() noexcept {
648 try {
649 lock_ref.lock();
650 } catch (std::exception&) {
651 }
652 }
653 } guard{lock};
654
655 auto s = std::chrono::time_point_cast<std::chrono::seconds>(atime);
656 auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(atime - s);
657
658 struct timespec ts = {static_cast<std::time_t>(s.time_since_epoch().count()),
659 static_cast<long>(ns.count())}; // NOLINT(runtime/int, google-runtime-int)
660
661 int ret = pthread_cond_timedwait(state->cv.native_handle(), internal_lock.mutex()->native_handle(), &ts);
662
663 return (ret == ETIMEDOUT) ? std::cv_status::timeout : std::cv_status::no_timeout;
664}
665
666/**
667 * @typedef condition_variable
668 * @brief Alias for @c ConditionVariable (monotonic-clock based).
669 */
671
672/**
673 * @typedef condition_variable_any
674 * @brief Alias for @c ConditionVariableAny (monotonic-clock based).
675 */
677
678} // namespace vlink
679
680#else
681
682namespace vlink {
683
684using ConditionVariable = std::condition_variable;
685using ConditionVariableAny = std::condition_variable_any;
688
689} // namespace vlink
690
691#endif