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