#ifndef CRUCIBLE_WORKQUEUE_H #define CRUCIBLE_WORKQUEUE_H #include #include #include #include #include #include #include namespace crucible { using namespace std; template class WorkQueue { public: using set_type = set; using key_type = Task; private: set_type m_set; mutable mutex m_mutex; condition_variable m_cond_full, m_cond_empty; size_t m_max_queue_depth; public: ~WorkQueue(); template WorkQueue(size_t max_queue_depth, Args... args); template WorkQueue(Args... args); void push(const key_type &name); void push_wait(const key_type &name, size_t limit); void push_nowait(const key_type &name); key_type pop(); bool pop_nowait(key_type &rv); key_type peek(); size_t size() const; bool empty(); set_type copy(); list peek(size_t count) const; }; template WorkQueue::~WorkQueue() { if (!m_set.empty()) { cerr << "ERROR: " << m_set.size() << " locked items still in WorkQueue " << this << " at destruction" << endl; } } template void WorkQueue::push(const key_type &name) { unique_lock lock(m_mutex); while (!m_set.count(name) && m_set.size() > m_max_queue_depth) { m_cond_full.wait(lock); } m_set.insert(name); m_cond_empty.notify_all(); } template void WorkQueue::push_wait(const key_type &name, size_t limit) { unique_lock lock(m_mutex); while (!m_set.count(name) && m_set.size() >= limit) { m_cond_full.wait(lock); } m_set.insert(name); m_cond_empty.notify_all(); } template void WorkQueue::push_nowait(const key_type &name) { unique_lock lock(m_mutex); m_set.insert(name); m_cond_empty.notify_all(); } template typename WorkQueue::key_type WorkQueue::pop() { unique_lock lock(m_mutex); while (m_set.empty()) { m_cond_empty.wait(lock); } key_type rv = *m_set.begin(); m_set.erase(m_set.begin()); m_cond_full.notify_all(); return rv; } template bool WorkQueue::pop_nowait(key_type &rv) { unique_lock lock(m_mutex); if (m_set.empty()) { return false; } rv = *m_set.begin(); m_set.erase(m_set.begin()); m_cond_full.notify_all(); return true; } template typename WorkQueue::key_type WorkQueue::peek() { unique_lock lock(m_mutex); if (m_set.empty()) { return key_type(); } else { return *m_set.begin(); } } template size_t WorkQueue::size() const { unique_lock lock(m_mutex); return m_set.size(); } template bool WorkQueue::empty() { unique_lock lock(m_mutex); return m_set.empty(); } template typename WorkQueue::set_type WorkQueue::copy() { unique_lock lock(m_mutex); return m_set; } template list WorkQueue::peek(size_t count) const { unique_lock lock(m_mutex); list rv; for (auto i : m_set) { if (count--) { rv.push_back(i); } else { break; } } return rv; } template template WorkQueue::WorkQueue(Args... args) : m_set(args...), m_max_queue_depth(numeric_limits::max()) { } template template WorkQueue::WorkQueue(size_t max_depth, Args... args) : m_set(args...), m_max_queue_depth(max_depth) { } } #endif // CRUCIBLE_WORKQUEUE_H