Gaia-ECS v0.9.3
A simple and powerful entity component system
Loading...
Searching...
No Matches
jobcommon.h
1#pragma once
2
3#include <cstddef>
4#include <inttypes.h>
5#include <new>
6#include <type_traits>
7#include <utility>
8
9#include "gaia/core/utility.h"
10#include "gaia/mem/mem_alloc.h"
11#include "gaia/mt/event.h"
12#include "gaia/mt/jobqueue.h"
13#include "gaia/util/small_func.h"
14
15namespace gaia {
16 namespace mt {
17 enum class JobPriority : uint8_t {
19 High = 0,
21 Low = 1
22 };
23 static inline constexpr uint32_t JobPriorityCnt = 2;
24
25 enum JobCreationFlags : uint8_t {
26 Default = 0,
28 ManualDelete = 0x01,
30 CanWait = 0x02,
32 Background = 0x04
33 };
34
35 struct JobAllocCtx {
36 JobPriority priority;
37 };
38
39 struct Job {
40 util::SmallFunc func;
41 JobPriority priority = JobPriority::High;
42 JobCreationFlags flags = JobCreationFlags::Default;
43 };
44
45 struct JobArgs {
46 uint32_t idxStart;
47 uint32_t idxEnd;
48 };
49
52 static constexpr uint32_t BufferSize = 24;
53
54 enum class Op : uint8_t { Invoke, Destroy, Move };
55 using OpFn = void (*)(Op op, JobArgsFunc* dst, JobArgsFunc* src, const JobArgs* pArgs);
56
57 OpFn m_func = nullptr;
58 alignas(std::max_align_t) uint8_t m_storage[BufferSize];
59
60 void destroy() {
61 if (m_func != nullptr) {
62 m_func(Op::Destroy, this, nullptr, nullptr);
63 m_func = nullptr;
64 }
65 }
66
67 template <typename F>
68 void init(F&& f) {
69 using Fn = std::decay_t<F>;
70 static_assert(std::is_invocable_r_v<void, Fn&, const JobArgs&>, "JobArgsFunc requires a compatible callable");
71 static_assert(std::is_move_constructible_v<Fn>, "Callable must be move-constructible");
72 static_assert(
73 alignof(Fn) <= alignof(std::max_align_t), "Over-aligned callables are not supported for JobArgsFunc");
74
75 if constexpr (sizeof(Fn) <= BufferSize) {
76 new (m_storage) Fn(GAIA_FWD(f));
77
78 m_func = [](Op op, JobArgsFunc* dst, JobArgsFunc* src, const JobArgs* pArgs) {
79 auto* pFn = reinterpret_cast<Fn*>(dst->m_storage);
80 switch (op) {
81 case Op::Invoke:
82 GAIA_ASSERT(pArgs != nullptr);
83 (*pFn)(*pArgs);
84 break;
85 case Op::Destroy:
86 if constexpr (!std::is_trivially_destructible_v<Fn>)
87 pFn->~Fn();
88 break;
89 case Op::Move: {
90 GAIA_ASSERT(src != nullptr);
91 auto* pSrcFn = reinterpret_cast<Fn*>(src->m_storage);
92 new (dst->m_storage) Fn(GAIA_MOV(*pSrcFn));
93 if constexpr (!std::is_trivially_destructible_v<Fn>)
94 pSrcFn->~Fn();
95 dst->m_func = src->m_func;
96 src->m_func = nullptr;
97 break;
98 }
99 }
100 };
101 } else {
102 auto* pStorage = mem::AllocHelper::alloc<Fn>();
103 GAIA_ASSERT((uintptr_t)pStorage % alignof(Fn) == 0);
104 auto* pFunc = new (pStorage) Fn(GAIA_FWD(f));
105 *reinterpret_cast<Fn**>(m_storage) = pFunc;
106
107 m_func = [](Op op, JobArgsFunc* dst, JobArgsFunc* src, const JobArgs* pArgs) {
108 auto*& pFn = *reinterpret_cast<Fn**>(dst->m_storage);
109 switch (op) {
110 case Op::Invoke:
111 GAIA_ASSERT(pArgs != nullptr);
112 GAIA_ASSERT(pFn != nullptr);
113 (*pFn)(*pArgs);
114 break;
115 case Op::Destroy:
116 GAIA_ASSERT(pFn != nullptr);
117 if constexpr (!std::is_trivially_destructible_v<Fn>)
118 pFn->~Fn();
119 mem::AllocHelper::free(pFn);
120 pFn = nullptr;
121 break;
122 case Op::Move:
123 GAIA_ASSERT(src != nullptr);
124 *reinterpret_cast<Fn**>(dst->m_storage) = *reinterpret_cast<Fn**>(src->m_storage);
125 dst->m_func = src->m_func;
126 *reinterpret_cast<Fn**>(src->m_storage) = nullptr;
127 src->m_func = nullptr;
128 break;
129 }
130 };
131 }
132 }
133
134 public:
135 JobArgsFunc() = default;
136 ~JobArgsFunc() {
137 destroy();
138 }
139
140 JobArgsFunc(const JobArgsFunc&) = delete;
141 JobArgsFunc& operator=(const JobArgsFunc&) = delete;
142
143 JobArgsFunc(JobArgsFunc&& other) noexcept {
144 if (other.m_func != nullptr)
145 other.m_func(Op::Move, this, &other, nullptr);
146 }
147
148 JobArgsFunc& operator=(JobArgsFunc&& other) noexcept {
149 if (this != &other) {
150 destroy();
151 if (other.m_func != nullptr)
152 other.m_func(Op::Move, this, &other, nullptr);
153 }
154 return *this;
155 }
156
157 template <typename F, typename = std::enable_if_t<!std::is_same_v<std::decay_t<F>, JobArgsFunc>>>
158 JobArgsFunc(F&& f) {
159 init(GAIA_FWD(f));
160 }
161
162 template <typename F, typename = std::enable_if_t<!std::is_same_v<std::decay_t<F>, JobArgsFunc>>>
163 JobArgsFunc& operator=(F&& f) {
164 destroy();
165 init(GAIA_FWD(f));
166 return *this;
167 }
168
169 template <typename F>
170 static JobArgsFunc create(F&& f) {
171 JobArgsFunc func;
172 func.init(GAIA_FWD(f));
173 return func;
174 }
175
176 void exec(const JobArgs& args) const {
177 GAIA_ASSERT(m_func != nullptr);
178 m_func(Op::Invoke, const_cast<JobArgsFunc*>(this), nullptr, &args);
179 }
180
181 void operator()(const JobArgs& args) const {
182 exec(args);
183 }
184
185 void reset() {
186 destroy();
187 }
188
189 explicit operator bool() const {
190 return m_func != nullptr;
191 }
192 };
193
194 struct JobParallel {
195 JobArgsFunc func;
196 JobPriority priority = JobPriority::High;
197 };
198
202 void* pCtx = nullptr;
203 void (*invoke)(void*, const JobArgs&) = nullptr;
204 JobPriority priority = JobPriority::High;
205 };
206
207 class ThreadPool;
208
209 struct ThreadCtx {
213 uint32_t workerIdx;
215 JobPriority prio;
217 bool background = false;
219 bool threadCreated = false;
223 JobQueue<512> jobQueue;
224
225 ThreadCtx() = default;
226 ~ThreadCtx() = default;
227
228 void reset() {
229 background = false;
230 threadCreated = false;
231 event.reset();
232 jobQueue.clear();
233 }
234
235 ThreadCtx(const ThreadCtx& other) = delete;
236 ThreadCtx& operator=(const ThreadCtx& other) = delete;
237 ThreadCtx(ThreadCtx&& other) = delete;
238 ThreadCtx& operator=(ThreadCtx&& other) = delete;
239 };
240 } // namespace mt
241} // namespace gaia
Manual-reset style synchronization primitive for waking a waiting thread. The event stays signaled un...
Definition event.h:20
Move-only callback wrapper specialized for parallel job ranges.
Definition jobcommon.h:51
Definition threadpool.h:87
Move-only function wrapper with inline storage and optional SmallBlockAllocator spill storage....
Definition small_func.h:19
Checks if endianess was detected correctly at compile-time.
Definition bitset.h:9
Definition jobcommon.h:35
Definition jobcommon.h:45
Non-owning callback descriptor for parallel jobs.
Definition jobcommon.h:201
Definition jobcommon.h:194
Definition jobcommon.h:39
Definition jobcommon.h:209
bool background
True when the worker executes background jobs.
Definition jobcommon.h:217
JobPriority prio
Job priority.
Definition jobcommon.h:215
uint32_t workerIdx
Worker index.
Definition jobcommon.h:213
Event event
Event signaled when a job is executed.
Definition jobcommon.h:221
ThreadPool * tp
Thread pool pointer.
Definition jobcommon.h:211
bool threadCreated
True when the worker thread has been successfully created.
Definition jobcommon.h:219
JobQueue< 512 > jobQueue
Lock-free work stealing queue for the jobs.
Definition jobcommon.h:223