Geant4-11
UserTaskQueue.icc
Go to the documentation of this file.
1//
2// MIT License
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.
18//
19// ---------------------------------------------------------------
20// Tasking class header
21// Class Description:
22// ---------------------------------------------------------------
23// Author: Jonathan Madsen
24// ---------------------------------------------------------------
25
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"
32
33#include <atomic>
34#include <cassert>
35#include <deque>
36#include <list>
37#include <memory>
38#include <queue>
39#include <stack>
40
41namespace PTL
42{
43class VTask;
44
45//======================================================================================//
46
47class TaskSubQueue
48{
49public:
50 template <typename Tp>
51 using container = std::list<Tp>;
52
53 typedef std::shared_ptr<VTask> task_pointer;
54 typedef container<task_pointer> container_type;
55 typedef container_type::size_type size_type;
56
57public:
58 TaskSubQueue(std::atomic_uintmax_t* _ntasks);
59 TaskSubQueue(const TaskSubQueue&);
60 ~TaskSubQueue();
61
62 TaskSubQueue& operator=(const TaskSubQueue&) = delete;
63
64public:
65 int GetId() const;
66
67 bool AcquireClaim();
68 void ReleaseClaim();
69
70 void PushTask(task_pointer&&) PTL_NO_SANITIZE_THREAD;
71 task_pointer PopTask(bool front = true) PTL_NO_SANITIZE_THREAD;
72
73 size_type size() const;
74 bool empty() const;
75
76private:
77 // mutex
78#if defined(PTL_USE_LOCKS)
79 Mutex m_mutex{};
80#endif
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;
87 // queue of tasks
88 container_type m_task_queue;
89};
90
91//======================================================================================//
92
93inline TaskSubQueue::TaskSubQueue(std::atomic_uintmax_t* _ntasks)
94: m_ntasks(0)
95, m_available(true)
96, m_all_tasks(_ntasks)
97{}
98
99//======================================================================================//
100
101inline TaskSubQueue::TaskSubQueue(const TaskSubQueue& rhs)
102: m_ntasks(0)
103, m_available(true)
104, m_all_tasks(rhs.m_all_tasks)
105{}
106
107//======================================================================================//
108
109inline TaskSubQueue::~TaskSubQueue() {}
110
111//======================================================================================//
112
113inline bool
114TaskSubQueue::AcquireClaim()
115{
116 bool is_avail = m_available.load(std::memory_order_relaxed);
117 if(!is_avail)
118 return false;
119 return m_available.compare_exchange_strong(is_avail, false,
120 std::memory_order_relaxed);
121}
122
123//======================================================================================//
124
125inline void
126TaskSubQueue::ReleaseClaim()
127{
128 // if(m_available.load(std::memory_order_relaxed))
129 // return;
130 m_available.store(true, std::memory_order_release);
131}
132
133//======================================================================================//
134
135inline TaskSubQueue::size_type
136TaskSubQueue::size() const
137{
138 return m_ntasks.load();
139}
140
141//======================================================================================//
142
143inline bool
144TaskSubQueue::empty() const
145{
146 return (m_ntasks.load() == 0);
147}
148
149//======================================================================================//
150
151inline void
152TaskSubQueue::PushTask(task_pointer&& task)
153{
154 // no need to lock these if claim is acquired via atomic
155 assert(m_available.load(std::memory_order_relaxed) == false);
156 ++m_ntasks;
157#if defined(PTL_USE_LOCKS)
158 AutoLock lk{ m_mutex };
159#endif
160 m_task_queue.emplace_front(std::move(task));
161}
162
163//======================================================================================//
164
165inline TaskSubQueue::task_pointer
166TaskSubQueue::PopTask(bool front)
167{
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)
171 return nullptr;
172
173 task_pointer _task{ nullptr };
174 if(front)
175 {
176#if defined(PTL_USE_LOCKS)
177 AutoLock lk{ m_mutex };
178#endif
179 _task = std::move(m_task_queue.front());
180 m_task_queue.pop_front();
181 }
182 else
183 {
184#if defined(PTL_USE_LOCKS)
185 AutoLock lk{ m_mutex };
186#endif
187 _task = std::move(m_task_queue.back());
188 m_task_queue.pop_back();
189 }
190 --m_ntasks;
191
192 return _task;
193}
194
195//======================================================================================//
196
197} // namespace PTL