// // MIT License // Copyright (c) 2020 Jonathan R. Madsen // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED // "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT // LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // // --------------------------------------------------------------- // Tasking class header // Class Description: // --------------------------------------------------------------- // Author: Jonathan Madsen // --------------------------------------------------------------- #include "PTL/AutoLock.hh" #include "PTL/Globals.hh" #include "PTL/ThreadPool.hh" #include "PTL/Threading.hh" #include "PTL/Types.hh" #include "PTL/VUserTaskQueue.hh" #include #include #include #include #include #include namespace PTL { class VTask; //======================================================================================// class TaskSubQueue { public: template using container = std::deque<_Tp>; typedef VTask* task_pointer; typedef container container_type; typedef container_type::size_type size_type; public: TaskSubQueue(std::atomic_uintmax_t* _ntasks); TaskSubQueue(const TaskSubQueue&); ~TaskSubQueue(); TaskSubQueue& operator=(const TaskSubQueue&) = delete; public: int GetId() const; bool AcquireClaim(); void ReleaseClaim(); void PushTask(task_pointer); task_pointer PopTask(bool front = true); size_type size() const; bool empty() const; private: // used internally to keep number of tasks std::atomic m_ntasks; // for checking if being modified std::atomic_bool m_available; // used my master queue to keep track of number of tasks std::atomic_uintmax_t* m_all_tasks; // queue of tasks container_type m_task_queue; }; //======================================================================================// inline TaskSubQueue::TaskSubQueue(std::atomic_uintmax_t* _ntasks) : m_ntasks(0) , m_available(true) , m_all_tasks(_ntasks) {} //======================================================================================// inline TaskSubQueue::TaskSubQueue(const TaskSubQueue& rhs) : m_ntasks(0) , m_available(true) , m_all_tasks(rhs.m_all_tasks) {} //======================================================================================// inline TaskSubQueue::~TaskSubQueue() {} //======================================================================================// inline bool TaskSubQueue::AcquireClaim() { bool is_avail = m_available.load(std::memory_order_relaxed); if(!is_avail) return false; return m_available.compare_exchange_strong(is_avail, false, std::memory_order_relaxed); } //======================================================================================// inline void TaskSubQueue::ReleaseClaim() { // if(m_available.load(std::memory_order_relaxed)) // return; m_available.store(true, std::memory_order_release); } //======================================================================================// inline TaskSubQueue::size_type TaskSubQueue::size() const { return m_ntasks.load(); } //======================================================================================// inline bool TaskSubQueue::empty() const { return (m_ntasks.load() == 0); } //======================================================================================// inline void TaskSubQueue::PushTask(task_pointer task) { // no need to lock these if claim is acquired via atomic assert(m_available.load(std::memory_order_relaxed) == false); //++(*m_all_tasks); already incremented ++m_ntasks; m_task_queue.push_front(task); } //======================================================================================// inline TaskSubQueue::task_pointer TaskSubQueue::PopTask(bool front) { // no need to lock -- claim is acquired via atomic assert(m_available.load(std::memory_order_relaxed) == false); if(m_task_queue.empty()) return nullptr; task_pointer _task; if(front) { _task = std::move(m_task_queue.front()); m_task_queue.pop_front(); } else { _task = std::move(m_task_queue.back()); m_task_queue.pop_back(); } --m_ntasks; return _task; } //======================================================================================// } // namespace PTL