3// Copyright (c) 2020 Jonathan R. Madsen
4// Permission is hereby granted, free of charge, to any person obtaining a copy
5// of this software and associated documentation files (the "Software"), to deal
6// in the Software without restriction, including without limitation the rights
7// to use, copy, modify, merge, publish, distribute, sublicense, and
8// copies of the Software, and to permit persons to whom the Software is
9// furnished to do so, subject to the following conditions:
10// The above copyright notice and this permission notice shall be included in
11// all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED
12// "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
13// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
15// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
16// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
17// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19// ---------------------------------------------------------------
20// Tasking class header
22// ---------------------------------------------------------------
23// Author: Jonathan Madsen
24// ---------------------------------------------------------------
26#include "PTL/AutoLock.hh"
27#include "PTL/Globals.hh"
28#include "PTL/ThreadPool.hh"
29#include "PTL/Threading.hh"
30#include "PTL/Types.hh"
31#include "PTL/VUserTaskQueue.hh"
45//======================================================================================//
50 template <typename Tp>
51 using container = std::list<Tp>;
53 typedef std::shared_ptr<VTask> task_pointer;
54 typedef container<task_pointer> container_type;
55 typedef container_type::size_type size_type;
58 TaskSubQueue(std::atomic_uintmax_t* _ntasks);
59 TaskSubQueue(const TaskSubQueue&);
62 TaskSubQueue& operator=(const TaskSubQueue&) = delete;
70 void PushTask(task_pointer&&) PTL_NO_SANITIZE_THREAD;
71 task_pointer PopTask(bool front = true) PTL_NO_SANITIZE_THREAD;
73 size_type size() const;
78#if defined(PTL_USE_LOCKS)
81 // used internally to keep number of tasks
82 std::atomic<size_type> m_ntasks;
83 // for checking if being modified
84 std::atomic_bool m_available;
85 // used my master queue to keep track of number of tasks
86 std::atomic_uintmax_t* m_all_tasks;
88 container_type m_task_queue;
91//======================================================================================//
93inline TaskSubQueue::TaskSubQueue(std::atomic_uintmax_t* _ntasks)
99//======================================================================================//
101inline TaskSubQueue::TaskSubQueue(const TaskSubQueue& rhs)
104, m_all_tasks(rhs.m_all_tasks)
107//======================================================================================//
109inline TaskSubQueue::~TaskSubQueue() {}
111//======================================================================================//
114TaskSubQueue::AcquireClaim()
116 bool is_avail = m_available.load(std::memory_order_relaxed);
119 return m_available.compare_exchange_strong(is_avail, false,
120 std::memory_order_relaxed);
123//======================================================================================//
126TaskSubQueue::ReleaseClaim()
128 // if(m_available.load(std::memory_order_relaxed))
130 m_available.store(true, std::memory_order_release);
133//======================================================================================//
135inline TaskSubQueue::size_type
136TaskSubQueue::size() const
138 return m_ntasks.load();
141//======================================================================================//
144TaskSubQueue::empty() const
146 return (m_ntasks.load() == 0);
149//======================================================================================//
152TaskSubQueue::PushTask(task_pointer&& task)
154 // no need to lock these if claim is acquired via atomic
155 assert(m_available.load(std::memory_order_relaxed) == false);
157#if defined(PTL_USE_LOCKS)
158 AutoLock lk{ m_mutex };
160 m_task_queue.emplace_front(std::move(task));
163//======================================================================================//
165inline TaskSubQueue::task_pointer
166TaskSubQueue::PopTask(bool front)
168 // no need to lock -- claim is acquired via atomic
169 assert(m_available.load(std::memory_order_relaxed) == false);
170 if(m_ntasks.load() == 0)
173 task_pointer _task{ nullptr };
176#if defined(PTL_USE_LOCKS)
177 AutoLock lk{ m_mutex };
179 _task = std::move(m_task_queue.front());
180 m_task_queue.pop_front();
184#if defined(PTL_USE_LOCKS)
185 AutoLock lk{ m_mutex };
187 _task = std::move(m_task_queue.back());
188 m_task_queue.pop_back();
195//======================================================================================//