#ifndef CRUCIBLE_TIMEQUEUE_H #define CRUCIBLE_TIMEQUEUE_H #include #include #include #include #include #include #include #include namespace crucible { using namespace std; template class TimeQueue { public: using Timestamp = chrono::high_resolution_clock::time_point; private: struct Item { Timestamp m_time; unsigned m_id; Task m_task; bool operator<(const Item &that) const { if (m_time < that.m_time) return true; if (that.m_time < m_time) return false; return m_id < that.m_id; } static unsigned s_id; Item(const Timestamp &time, const Task& task) : m_time(time), m_id(++s_id), m_task(task) { } }; set m_set; mutable mutex m_mutex; condition_variable m_cond_full, m_cond_empty; size_t m_max_queue_depth; public: ~TimeQueue(); TimeQueue(size_t max_queue_depth = numeric_limits::max()); void push(const Task &task, double delay = 0); void push_nowait(const Task &task, double delay = 0); Task pop(); bool pop_nowait(Task &t); double when() const; size_t size() const; bool empty() const; list peek(size_t count) const; }; template unsigned TimeQueue::Item::s_id = 0; template TimeQueue::~TimeQueue() { if (!m_set.empty()) { cerr << "ERROR: " << m_set.size() << " locked items still in TimeQueue at destruction" << endl; } } template void TimeQueue::push(const Task &task, double delay) { Timestamp time = chrono::high_resolution_clock::now() + chrono::duration_cast(chrono::duration(delay)); unique_lock lock(m_mutex); while (m_set.size() > m_max_queue_depth) { m_cond_full.wait(lock); } m_set.insert(Item(time, task)); m_cond_empty.notify_all(); } template void TimeQueue::push_nowait(const Task &task, double delay) { Timestamp time = chrono::high_resolution_clock::now() + chrono::duration_cast(chrono::duration(delay)); unique_lock lock(m_mutex); m_set.insert(Item(time, task)); m_cond_empty.notify_all(); } template Task TimeQueue::pop() { unique_lock lock(m_mutex); while (1) { while (m_set.empty()) { m_cond_empty.wait(lock); } Timestamp now = chrono::high_resolution_clock::now(); if (now > m_set.begin()->m_time) { Task rv = m_set.begin()->m_task; m_set.erase(m_set.begin()); m_cond_full.notify_all(); return rv; } m_cond_empty.wait_until(lock, m_set.begin()->m_time); } } template bool TimeQueue::pop_nowait(Task &t) { unique_lock lock(m_mutex); if (m_set.empty()) { return false; } Timestamp now = chrono::high_resolution_clock::now(); if (now <= m_set.begin()->m_time) { return false; } t = m_set.begin()->m_task; m_set.erase(m_set.begin()); m_cond_full.notify_all(); return true; } template double TimeQueue::when() const { unique_lock lock(m_mutex); if (m_set.empty()) { return numeric_limits::infinity(); } return chrono::duration(m_set.begin()->m_time - chrono::high_resolution_clock::now()).count(); } template size_t TimeQueue::size() const { unique_lock lock(m_mutex); return m_set.size(); } template bool TimeQueue::empty() const { unique_lock lock(m_mutex); return m_set.empty(); } template list TimeQueue::peek(size_t count) const { unique_lock lock(m_mutex); list rv; auto it = m_set.begin(); while (count-- && it != m_set.end()) { rv.push_back(it->m_task); ++it; } return rv; } template TimeQueue::TimeQueue(size_t max_depth) : m_max_queue_depth(max_depth) { } } #endif // CRUCIBLE_TIMEQUEUE_H