1
0

condition_variable 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. #pragma once
  2. #if __cplusplus < 201103L
  3. #error "C++ version lower than C++11"
  4. #endif
  5. #include <pthread.h>
  6. #include <system_error>
  7. #include <chrono>
  8. #include <utility>
  9. #include <functional>
  10. #include <memory>
  11. #include "__utils.h"
  12. #include "mutex"
  13. #define rt_cpp_cond_var pthread_cond_t
  14. namespace std
  15. {
  16. enum class cv_status
  17. {
  18. no_timeout,
  19. timeout
  20. };
  21. class condition_variable
  22. {
  23. public:
  24. typedef rt_cpp_cond_var *native_handle_type;
  25. condition_variable(const condition_variable &) = delete;
  26. condition_variable &operator=(const condition_variable &) = delete;
  27. condition_variable() = default;
  28. ~condition_variable()
  29. {
  30. pthread_cond_destroy(&_m_cond);
  31. }
  32. void wait(unique_lock<mutex> &lock);
  33. void notify_one() noexcept
  34. {
  35. pthread_cond_signal(&_m_cond);
  36. }
  37. void notify_all() noexcept
  38. {
  39. pthread_cond_broadcast(&_m_cond);
  40. }
  41. template <class Predicate>
  42. void wait(unique_lock<mutex> &lock, Predicate pred)
  43. {
  44. while (!pred())
  45. wait(lock);
  46. }
  47. template <class Clock, class Duration>
  48. cv_status wait_until(unique_lock<mutex> &lock,
  49. const chrono::time_point<Clock, Duration> &abs_time)
  50. {
  51. if (!lock.owns_lock())
  52. throw_system_error((int)errc::operation_not_permitted,
  53. "condition_variable::wailt_until: waiting on unlocked lock");
  54. auto secs = chrono::time_point_cast<chrono::seconds>(abs_time);
  55. auto nano_secs = chrono::duration_cast<chrono::nanoseconds>(abs_time - secs);
  56. struct timespec c_abs_time = {static_cast<time_t>(secs.time_since_epoch().count()),
  57. static_cast<long>(nano_secs.count())};
  58. pthread_cond_timedwait(&_m_cond, lock.mutex()->native_handle(), &c_abs_time);
  59. return (Clock::now() < abs_time) ? cv_status::no_timeout : cv_status::timeout;
  60. }
  61. template <class Clock, class Duration, class Predicate>
  62. bool wait_until(unique_lock<mutex> &lock,
  63. const chrono::time_point<Clock, Duration> &abs_time,
  64. Predicate pred)
  65. {
  66. while (!pred())
  67. if (wait_until(lock, abs_time) == cv_status::timeout)
  68. return pred();
  69. return true;
  70. }
  71. template <class Rep, class Period>
  72. cv_status wait_for(unique_lock<mutex> &lock,
  73. const chrono::duration<Rep, Period> &rel_time)
  74. {
  75. return wait_until(lock, real_time_clock::now() + rel_time);
  76. }
  77. template <class Rep, class Period, class Predicate>
  78. bool wait_for(unique_lock<mutex> &lock,
  79. const chrono::duration<Rep, Period> &rel_time,
  80. Predicate pred)
  81. {
  82. return wait_until(lock, real_time_clock::now() + rel_time, std::move(pred));
  83. }
  84. native_handle_type native_handle()
  85. {
  86. return &_m_cond;
  87. }
  88. private:
  89. rt_cpp_cond_var _m_cond = PTHREAD_COND_INITIALIZER;
  90. };
  91. // Lockable is only required to have `lock()` and `unlock()`
  92. class condition_variable_any
  93. {
  94. private:
  95. condition_variable _m_cond;
  96. shared_ptr<mutex> _m_mtx;
  97. // so that Lockable automatically unlocks when waiting and locks after waiting
  98. template <class Lockable>
  99. struct unlocker
  100. {
  101. Lockable &_m_lock;
  102. explicit unlocker(Lockable &lk)
  103. : _m_lock(lk)
  104. {
  105. _m_lock.unlock();
  106. }
  107. ~unlocker()
  108. {
  109. _m_lock.lock();
  110. }
  111. unlocker(const unlocker &) = delete;
  112. unlocker &operator=(const unlocker &) = delete;
  113. };
  114. public:
  115. condition_variable_any() : _m_mtx(std::make_shared<mutex>()) {}
  116. ~condition_variable_any() = default;
  117. condition_variable_any(const condition_variable_any &) = delete;
  118. condition_variable_any &operator=(const condition_variable_any &) = delete;
  119. void notify_one() noexcept
  120. {
  121. lock_guard<mutex> lk(*_m_mtx);
  122. _m_cond.notify_one();
  123. }
  124. void notify_all() noexcept
  125. {
  126. lock_guard<mutex> lk(*_m_mtx);
  127. _m_cond.notify_all();
  128. }
  129. template <class Lock>
  130. void wait(Lock &lock)
  131. {
  132. shared_ptr<mutex> mut = _m_mtx;
  133. unique_lock<mutex> lk(*mut);
  134. unlocker<Lock> auto_lk(lock); // unlock here
  135. unique_lock<mutex> lk2(std::move(lk));
  136. _m_cond.wait(lk2);
  137. } // mut.unlock(); lock.lock();
  138. template <class Lock, class Predicate>
  139. void wait(Lock &lock, Predicate pred)
  140. {
  141. while (!pred())
  142. wait(lock);
  143. }
  144. template <class Lock, class Clock, class Duration>
  145. cv_status wait_until(Lock &lock,
  146. const chrono::time_point<Clock, Duration> &abs_time)
  147. {
  148. shared_ptr<mutex> mut = _m_mtx;
  149. unique_lock<mutex> lk(*mut);
  150. unlocker<Lock> auto_lk(lock); // unlock here
  151. unique_lock<mutex> lk2(std::move(lk));
  152. return _m_cond.wait_until(lk2, abs_time);
  153. }
  154. template <class Lock, class Clock, class Duration, class Predicate>
  155. bool wait_until(Lock &lock,
  156. const chrono::time_point<Clock, Duration> &abs_time,
  157. Predicate pred)
  158. {
  159. while (!pred())
  160. if (wait_until(lock, abs_time) == cv_status::timeout)
  161. return pred();
  162. return true;
  163. }
  164. template <class Lock, class Rep, class Period>
  165. cv_status wait_for(Lock &lock,
  166. const chrono::duration<Rep, Period> &rel_time)
  167. {
  168. return wait_until(lock, real_time_clock::now() + rel_time);
  169. }
  170. template <class Lock, class Rep, class Period, class Predicate>
  171. bool wait_for(Lock &lock,
  172. const chrono::duration<Rep, Period> &rel_time,
  173. Predicate pred)
  174. {
  175. return wait_until(lock, real_time_clock::now() + rel_time, std::move(pred));
  176. }
  177. };
  178. void notify_all_at_thread_exit(condition_variable &cond, unique_lock<mutex> lk);
  179. } // namespace std