3#include "gaia/config/config.h"
7#include "gaia/cnt/sarray.h"
8#include "gaia/config/profiler.h"
9#include "gaia/core/utility.h"
10#include "gaia/mt/jobhandle.h"
14GAIA_MSVC_WARNING_PUSH()
15GAIA_MSVC_WARNING_DISABLE(4324)
22 template <const u
int32_t N = 1 << 12>
24 static_assert(N >= 2);
25 static_assert((N & (N - 1)) == 0, "Extent of JobQueue must be a power of 2");
26 static constexpr u
int32_t MASK = N - 1;
28 static_assert(sizeof(std::atomic_u
int32_t) == sizeof(JobHandle));
29 cnt::sarray<std::atomic_u
int32_t, N> m_buffer;
30 GAIA_ALIGNAS(GAIA_CACHELINE_SIZE) std::atomic_u
int32_t m_bottom;
31 GAIA_ALIGNAS(GAIA_CACHELINE_SIZE) std::atomic_u
int32_t m_top;
38 ~JobQueue() = default;
39 JobQueue(const JobQueue&) = default;
40 JobQueue& operator=(const JobQueue&) = default;
41 JobQueue(JobQueue&&) noexcept = default;
42 JobQueue& operator=(JobQueue&&) noexcept = default;
47 for (auto& val: m_buffer)
48 val.store(((JobHandle)JobNull_t()).value());
54 GAIA_PROF_SCOPE(JobQueue::empty);
56 const u
int32_t b = m_bottom.load(std::memory_order_relaxed);
57 const u
int32_t t = m_top.load(std::memory_order_relaxed);
58 return
int32_t(b - t) <= 0;
64 GAIA_NODISCARD
bool try_push(JobHandle jobHandle) {
65 GAIA_PROF_SCOPE(JobQueue::try_push);
67 const u
int32_t b = m_bottom.load(std::memory_order_relaxed);
68 const u
int32_t t = m_top.load(std::memory_order_acquire);
69 const u
int32_t used = b - t;
73 m_buffer[b & MASK].store(jobHandle.value(), std::memory_order_relaxed);
75 m_bottom.store(b + 1, std::memory_order_release);
83 GAIA_NODISCARD u
int32_t try_push(std::span<JobHandle> jobHandles) {
84 GAIA_PROF_SCOPE(JobQueue::try_push);
86 const u
int32_t cnt = (u
int32_t)jobHandles.size();
87 u
int32_t b = m_bottom.load(std::memory_order_relaxed);
88 const u
int32_t t = m_top.load(std::memory_order_acquire);
89 const u
int32_t used = b - t;
90 const u
int32_t free = (MASK + 1) - used;
91 const u
int32_t freeFinal = core::get_min(cnt, free);
93 for (u
int32_t i = 0; i < freeFinal; i++, b++)
94 m_buffer[b & MASK].store(jobHandles[i].value(), std::memory_order_relaxed);
96 m_bottom.store(b, std::memory_order_release);
104 GAIA_NODISCARD
bool try_pop(JobHandle& jobHandle) {
105 GAIA_PROF_SCOPE(JobQueue::try_pop);
107 u
int32_t jobHandleValue = ((JobHandle)JobNull_t{}).value();
109 const u
int32_t b = m_bottom.load(std::memory_order_relaxed) - 1;
110 m_bottom.store(b, std::memory_order_relaxed);
111 std::atomic_thread_fence(std::memory_order_seq_cst);
112 u
int32_t t = m_top.load(std::memory_order_relaxed);
114 if (
int(t - b) <= 0) {
116 jobHandleValue = m_buffer[b & MASK].load(std::memory_order_relaxed);
121 m_top.compare_exchange_strong(t, t + 1, std::memory_order_seq_cst, std::memory_order_relaxed);
122 m_bottom.store(b + 1, std::memory_order_relaxed);
123 jobHandle = JobHandle(jobHandleValue);
124 GAIA_ASSERT(jobHandle != (JobHandle)JobNull_t{});
128 jobHandle = JobHandle(jobHandleValue);
129 GAIA_ASSERT(jobHandle != (JobHandle)JobNull_t{});
134 m_bottom.store(b + 1, std::memory_order_relaxed);
141 GAIA_NODISCARD
bool try_steal(JobHandle& jobHandle) {
142 GAIA_PROF_SCOPE(JobQueue::try_steal);
144 u
int32_t t = m_top.load(std::memory_order_acquire);
145 std::atomic_thread_fence(std::memory_order_seq_cst);
146 const u
int32_t b = m_bottom.load(std::memory_order_acquire);
148 if (
int(b - t) <= 0) {
149 jobHandle = (JobHandle)JobNull_t{};
153 const u
int32_t jobHandleValue = m_buffer[t & MASK].load(std::memory_order_relaxed);
156 const
bool ret = m_top.compare_exchange_strong(t, t + 1, std::memory_order_seq_cst, std::memory_order_relaxed);
157 jobHandle = JobHandle(jobHandleValue);
158 GAIA_ASSERT(jobHandle != (JobHandle)JobNull_t{});
165 template <
class T, const u
int32_t N = 1 << 12>
167 static_assert(N >= 2);
168 static_assert((N & (N - 1)) == 0, "Extent of MpmcQueue must be a power of 2");
169 static constexpr u
int32_t MASK = N - 1;
172 std::atomic_u
int32_t sequence{};
175 using view_policy = mem::data_view_policy_aos<Node>;
177 static constexpr u
int32_t extent = N;
178 static constexpr u
int32_t allocated_
bytes = view_policy::get_min_
byte_size(0, N);
182 GAIA_MSVC_WARNING_PUSH()
183 GAIA_MSVC_WARNING_DISABLE(4324)
185 mem::raw_data_holder<Node, allocated_
bytes> m_data;
186 GAIA_ALIGNAS(GAIA_CACHELINE_SIZE) std::atomic_u
int32_t m_pushPos;
187 GAIA_ALIGNAS(GAIA_CACHELINE_SIZE) std::atomic_u
int32_t m_popPos;
189 GAIA_MSVC_WARNING_POP()
199 MpmcQueue(MpmcQueue&&) = delete;
200 MpmcQueue(const MpmcQueue&) = delete;
201 MpmcQueue& operator=(MpmcQueue&&) = delete;
202 MpmcQueue& operator=(const MpmcQueue&) = delete;
205 GAIA_NODISCARD constexpr Node* data() noexcept {
206 return GAIA_ACC((Node*)&m_data[0]);
209 GAIA_NODISCARD constexpr const Node* data() const noexcept {
210 return GAIA_ACC((const Node*)&m_data[0]);
214 Node* pNodes = data();
217 Node* pNode = &pNodes[i];
218 core::call_ctor(&pNode->sequence, i);
221 m_pushPos.store(0, std::memory_order_relaxed);
222 m_popPos.store(0, std::memory_order_relaxed);
226 Node* pNodes = data();
228 uint32_t enqPos = m_pushPos.load(std::memory_order_relaxed);
229 uint32_t deqPos = m_popPos.load(std::memory_order_relaxed);
230 for (uint32_t pos = deqPos; pos != enqPos; ++pos) {
231 Node* pNode = &pNodes[pos & MASK];
232 if (pNode->sequence.load(std::memory_order_relaxed) == pos + 1)
237 Node* pNode = &pNodes[i];
238 core::call_dtor(&pNode->sequence);
246 GAIA_PROF_SCOPE(MpmcQueue::empty);
248 const uint32_t pos = m_popPos.load(std::memory_order_relaxed);
249 const auto* pNode = &data()[pos & MASK];
250 const uint32_t seq = pNode->sequence.load(std::memory_order_acquire);
257 template <
typename TT>
258 bool try_push(TT&& item) {
259 GAIA_PROF_SCOPE(MpmcQueue::try_push);
261 Node* pNodes = data();
263 Node* pNode =
nullptr;
264 uint32_t pos = m_pushPos.load(std::memory_order_relaxed);
266 pNode = &pNodes[pos & MASK];
267 uint32_t seq = pNode->sequence.load(std::memory_order_acquire);
268 int32_t diff = int32_t(seq) - int32_t(pos);
270 if (m_pushPos.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed))
272 }
else if (diff < 0) {
276 pos = m_pushPos.load(std::memory_order_relaxed);
280 core::call_ctor(&pNode->item, GAIA_FWD(item));
281 pNode->sequence.store(pos + 1, std::memory_order_release);
288 bool try_pop(T& item) {
289 GAIA_PROF_SCOPE(MpmcQueue::try_pop);
291 Node* pNodes = data();
293 Node* pNode =
nullptr;
294 uint32_t pos = m_popPos.load(std::memory_order_relaxed);
296 pNode = &pNodes[pos & MASK];
297 uint32_t seq = pNode->sequence.load(std::memory_order_acquire);
298 int32_t diff = int32_t(seq) - int32_t(pos + 1);
300 if (m_popPos.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed))
302 }
else if (diff < 0) {
306 pos = m_popPos.load(std::memory_order_relaxed);
310 item = GAIA_MOV(pNode->item);
311 core::call_dtor(&pNode->item);
312 pNode->sequence.store(pos + MASK + 1, std::memory_order_release);
319GAIA_MSVC_WARNING_POP()