/*----------------------------------------------------------------------------*/ /* Copyright (c) FIRST 2016-2017. All Rights Reserved. */ /* Open Source Software - may be modified and shared by FRC teams. The code */ /* must be accompanied by the FIRST BSD license file in the root directory of */ /* the project. */ /*----------------------------------------------------------------------------*/ #pragma once /* std::condition_variable provides the native_handle() method to return its * underlying pthread_cond_t*. WPILib uses this to interface with the FRC * network communication library. Since WPILib uses a custom mutex class and * not std::mutex, std::condition_variable_any must be used instead. * std::condition_variable_any doesn't expose its internal handle, so this * class provides the same interface and implementation in addition to a * native_handle() method. */ #include #include #include #include "priority_mutex.h" class priority_condition_variable { typedef std::chrono::system_clock clock; public: typedef std::condition_variable::native_handle_type native_handle_type; priority_condition_variable() : m_mutex(std::make_shared()) {} ~priority_condition_variable() = default; priority_condition_variable(const priority_condition_variable&) = delete; priority_condition_variable& operator=(const priority_condition_variable&) = delete; void notify_one() noexcept { std::lock_guard lock(*m_mutex); m_cond.notify_one(); } void notify_all() noexcept { std::lock_guard lock(*m_mutex); m_cond.notify_all(); } template void wait(Lock& _lock) { std::shared_ptr _mutex = m_mutex; std::unique_lock my_lock(*_mutex); Unlock unlock(_lock); // *mutex must be unlocked before re-locking _lock so move // ownership of *_mutex lock to an object with shorter lifetime. std::unique_lock my_lock2(std::move(my_lock)); m_cond.wait(my_lock2); } template void wait(Lock& lock, Predicate p) { while (!p()) { wait(lock); } } template std::cv_status wait_until( Lock& _lock, const std::chrono::time_point& atime) { std::shared_ptr _mutex = m_mutex; std::unique_lock my_lock(*_mutex); Unlock unlock(_lock); // *_mutex must be unlocked before re-locking _lock so move // ownership of *_mutex lock to an object with shorter lifetime. std::unique_lock my_lock2(std::move(my_lock)); return m_cond.wait_until(my_lock2, atime); } template bool wait_until(Lock& lock, const std::chrono::time_point& atime, Predicate p) { while (!p()) { if (wait_until(lock, atime) == std::cv_status::timeout) { return p(); } } return true; } template std::cv_status wait_for(Lock& lock, const std::chrono::duration& rtime) { return wait_until(lock, clock::now() + rtime); } template bool wait_for(Lock& lock, const std::chrono::duration& rtime, Predicate p) { return wait_until(lock, clock::now() + rtime, std::move(p)); } native_handle_type native_handle() { return m_cond.native_handle(); } private: std::condition_variable m_cond; std::shared_ptr m_mutex; // scoped unlock - unlocks in ctor, re-locks in dtor template struct Unlock { explicit Unlock(Lock& lk) : m_lock(lk) { lk.unlock(); } ~Unlock() /*noexcept(false)*/ { if (std::uncaught_exception()) { try { m_lock.lock(); } catch (...) { } } else { m_lock.lock(); } } Unlock(const Unlock&) = delete; Unlock& operator=(const Unlock&) = delete; Lock& m_lock; }; };