#pragma once #if __cplusplus < 201103L #error "C++ version lower than C++11" #endif #include #include #include #include #include #include #include "__utils.h" #include "mutex" #define rt_cpp_cond_var pthread_cond_t namespace std { enum class cv_status { no_timeout, timeout }; class condition_variable { public: typedef rt_cpp_cond_var *native_handle_type; condition_variable(const condition_variable &) = delete; condition_variable &operator=(const condition_variable &) = delete; condition_variable() = default; ~condition_variable() { pthread_cond_destroy(&_m_cond); } void wait(unique_lock &lock); void notify_one() noexcept { pthread_cond_signal(&_m_cond); } void notify_all() noexcept { pthread_cond_broadcast(&_m_cond); } template void wait(unique_lock &lock, Predicate pred) { while (!pred()) wait(lock); } template cv_status wait_until(unique_lock &lock, const chrono::time_point &abs_time) { if (!lock.owns_lock()) throw_system_error((int)errc::operation_not_permitted, "condition_variable::wailt_until: waiting on unlocked lock"); auto secs = chrono::time_point_cast(abs_time); auto nano_secs = chrono::duration_cast(abs_time - secs); struct timespec c_abs_time = {static_cast(secs.time_since_epoch().count()), static_cast(nano_secs.count())}; pthread_cond_timedwait(&_m_cond, lock.mutex()->native_handle(), &c_abs_time); return (Clock::now() < abs_time) ? cv_status::no_timeout : cv_status::timeout; } template bool wait_until(unique_lock &lock, const chrono::time_point &abs_time, Predicate pred) { while (!pred()) if (wait_until(lock, abs_time) == cv_status::timeout) return pred(); return true; } template cv_status wait_for(unique_lock &lock, const chrono::duration &rel_time) { return wait_until(lock, real_time_clock::now() + rel_time); } template bool wait_for(unique_lock &lock, const chrono::duration &rel_time, Predicate pred) { return wait_until(lock, real_time_clock::now() + rel_time, std::move(pred)); } native_handle_type native_handle() { return &_m_cond; } private: rt_cpp_cond_var _m_cond = PTHREAD_COND_INITIALIZER; }; // Lockable is only required to have `lock()` and `unlock()` class condition_variable_any { private: condition_variable _m_cond; shared_ptr _m_mtx; // so that Lockable automatically unlocks when waiting and locks after waiting template struct unlocker { Lockable &_m_lock; explicit unlocker(Lockable &lk) : _m_lock(lk) { _m_lock.unlock(); } ~unlocker() { _m_lock.lock(); } unlocker(const unlocker &) = delete; unlocker &operator=(const unlocker &) = delete; }; public: condition_variable_any() : _m_mtx(std::make_shared()) {} ~condition_variable_any() = default; condition_variable_any(const condition_variable_any &) = delete; condition_variable_any &operator=(const condition_variable_any &) = delete; void notify_one() noexcept { lock_guard lk(*_m_mtx); _m_cond.notify_one(); } void notify_all() noexcept { lock_guard lk(*_m_mtx); _m_cond.notify_all(); } template void wait(Lock &lock) { shared_ptr mut = _m_mtx; unique_lock lk(*mut); unlocker auto_lk(lock); // unlock here unique_lock lk2(std::move(lk)); _m_cond.wait(lk2); } // mut.unlock(); lock.lock(); template void wait(Lock &lock, Predicate pred) { while (!pred()) wait(lock); } template cv_status wait_until(Lock &lock, const chrono::time_point &abs_time) { shared_ptr mut = _m_mtx; unique_lock lk(*mut); unlocker auto_lk(lock); // unlock here unique_lock lk2(std::move(lk)); return _m_cond.wait_until(lk2, abs_time); } template bool wait_until(Lock &lock, const chrono::time_point &abs_time, Predicate pred) { while (!pred()) if (wait_until(lock, abs_time) == cv_status::timeout) return pred(); return true; } template cv_status wait_for(Lock &lock, const chrono::duration &rel_time) { return wait_until(lock, real_time_clock::now() + rel_time); } template bool wait_for(Lock &lock, const chrono::duration &rel_time, Predicate pred) { return wait_until(lock, real_time_clock::now() + rel_time, std::move(pred)); } }; void notify_all_at_thread_exit(condition_variable &cond, unique_lock lk); } // namespace std