27#ifndef JOB_SYS_ASSERTIONS
28#define JOB_SYS_ASSERTIONS 1
32#define JobAssert(expr, msg) (::job::internal::AssertHandler)((expr), __FILE__, __LINE__, msg)
34#define JobAssert(expr, msg)
88 std::uint16_t num_threads = 0;
89 std::uint16_t normal_queue_size = 1024;
90 std::uint16_t worker_queue_size = 512;
91 std::uint64_t job_steal_rng_seed = 0u;
120 void Initialize(
const JobSystemMemoryRequirements& memory_requirements = {},
void*
const memory =
nullptr)
noexcept;
197 template<typename Closure>
198 void Dispatch(const
char* const name, Counter* const counter, const Closure& Callback, const
QueueMode queue =
QueueMode::Default) noexcept;
208 void WaitOn(const Counter& counter) noexcept;
228 void* src_user_data =
nullptr;
229 std::atomic_bool* is_ready_for_gc =
nullptr;
231 void ReleaseTaskToPool()
const;
236#if JOB_SYS_ASSERTIONS
237 void AssertHandler(
const bool condition,
const char*
const filename,
const int line_number,
const char*
const msg);
244 const std::size_t user_data_size,
245 const std::size_t user_data_alignment,
246 const void*
const user_data,
247 void (*InitUserData)(
void*
const user_data,
const void*
const in_user_data))
noexcept;
250 template<
typename Closure>
254 alignas(Closure)
unsigned char local_user_data[
sizeof(Closure)];
255 Closure*
const src_user_data =
static_cast<Closure*
>(ctx.src_user_data);
256 Closure*
const typed_callback = ::new (local_user_data) Closure(std::move(*src_user_data));
258 src_user_data->~Closure();
259 ctx.ReleaseTaskToPool();
261 (*typed_callback)(
static_cast<const job::Ctx&
>(ctx));
262 typed_callback->~Closure();
265 internal::DispatchImpl(name, counter, queue, ErasedCallback,
sizeof(Closure),
alignof(Closure), &Callback, +[](
void*
const dst_user_data,
const void*
const src_user_data) ->
void {
266 ::new (dst_user_data) Closure(*
static_cast<const Closure*
>(src_user_data));
295 static Splitter EvenSplit(
const std::size_t total_num_items, std::size_t num_groups_per_thread = 1u)
297 if (num_groups_per_thread < 1u)
299 num_groups_per_thread = 1u;
313 return Splitter{max_data_size /
sizeof(T)};
353 template<
typename F,
typename S>
356 job::Dispatch(name, counter, [=, splitter = std::forward<S>(splitter), fn = std::forward<F>(fn)](
const job::Ctx& ctx) ->
void {
357 if (count > 1u && splitter(count))
359 const std::size_t left_count = count / 2;
360 const std::size_t right_count = count - left_count;
367 for (std::size_t offset = 0u; offset < count; ++offset)
369 fn(ctx, start + offset);
374 template<
typename T,
typename F,
typename S>
377 return job::ParallelFor(name, counter, std::size_t(0), count, std::forward<S>(splitter), [=](
const job::Ctx& ctx,
const std::size_t index) { fn(ctx, data + index); }, queue);
397 template<
typename... F>
400 (
job::Dispatch(name, counter, std::forward<F>(fns), queue), ...);
403 template<
typename Splitter,
typename Reducer>
406 const auto ParallelReduce_Impl = [=, splitter = std::forward<Splitter>(splitter), reduce = std::forward<Reducer>(reduce)](
const job::Ctx& ctx) ->
void {
413 std::size_t count_left = count;
415 while (count_left > 1)
417 const std::size_t stride = count_left / 2;
419 const auto ReduceRange = [stride, &reduce](
const job::Ctx& ctx,
const std::size_t index) ->
void {
420 reduce(ctx, index, index + stride);
424 ParallelFor(name, &c, start, stride, splitter, ReduceRange, queue);
427 if ((count_left & 1) != 0)
429 reduce(ctx, start, start + count_left - 1);
void(*)(const PrivateCtx &ctx) JobFn
void AssertHandler(const bool condition, const char *const filename, const int line_number, const char *const msg)
void DispatchImpl(const char *const name, Counter *const counter, const QueueMode queue, const JobFn func, const std::size_t user_data_size, const std::size_t user_data_alignment, const void *const user_data, void(*InitUserData)(void *const user_data, const void *const in_user_data)) noexcept
void ParallelInvoke(const char *const name, Counter *const counter, const QueueMode queue, F &&... fns)
Invokes each passed in function object in parallel.
void WaitOn(const Counter &counter) noexcept
Blocks until all tasks associated with counter are done while This function will block but do work wh...
void Shutdown() noexcept
This will deallocate any memory used by the system and shutdown any threads created by 'bfjob::initia...
void ParallelReduce(const char *const name, Counter *const counter, const std::size_t start, const std::size_t count, Splitter &&splitter, Reducer &&reduce, const QueueMode queue=QueueMode::Default)
bool IsMainThread() noexcept
Allows for querying if we are currently executing in the main thread.
void ParallelFor(const char *const name, Counter *const counter, const std::size_t start, const std::size_t count, S &&splitter, F &&fn, const QueueMode queue=QueueMode::Default)
Parallel for algorithm, splits the work up recursively splitting based on the splitter passed in.
QueueMode
Determines which threads the task will be allowed to run on.
@ Default
Tasks in this queue will run on either the main or worker threads.
@ WorkerOnly
Tasks in this queue will never run on the main thread.
void Dispatch(const char *const name, Counter *const counter, const Closure &Callback, const QueueMode queue=QueueMode::Default) noexcept
Main API entrypoint, Pushes a task onto the queue.
std::atomic_uint64_t unfinished_tasks
std::uint16_t WorkerID
The id type of each worker thread.
WorkerID CurrentWorker() noexcept
The current id of the current thread. This function can be called by any thread concurrently.
const char * ProcessorArchitectureName() noexcept
An implementation defined name for the CPU architecture of the device. This function can be called by...
std::uint16_t NumWorkers() noexcept
Returns the number of workers created by the system. This function can be called by any thread concur...
void PauseProcessor() noexcept
CPU pause instruction to indicate when you are in a spin wait loop.
void Initialize(const JobSystemMemoryRequirements &memory_requirements={}, void *const memory=nullptr) noexcept
Sets up the Job system and creates all the worker threads. The thread that calls 'job::Initialize' is...
std::size_t NumSystemThreads() noexcept
Makes system calls to grab the number threads / processors on the device. This function can be called...
void YieldTimeSlice() noexcept
Asks the OS to yield this threads execution to another thread on the current cpu core.
The only syncronization mechanism. Allows you to wait on tasks you asssociated with this counter.
The runtime configuration for the Job System.
WorkerID current_worker
The worker the current task is running on.
Counter * task_counter
The counter this task will decrement when done.
const char * task_name
The debug name of the task.
The memory requirements for a given configuration JobSystemCreateOptions.
std::size_t byte_size
The number of bytes the job system needed.
JobSystemMemoryRequirements(const JobSystemCreateOptions &options={}) noexcept
std::size_t alignment
The base alignment the pointer should be.
JobSystemCreateOptions options
The options used to create the memory requirements.
static constexpr Splitter MaxItemsPerTask(const std::size_t max_items)
constexpr bool operator()(const std::size_t count) const
static Splitter EvenSplit(const std::size_t total_num_items, std::size_t num_groups_per_thread=1u)
Splits work evenly across the threads depending on the number of workers.
static constexpr Splitter MaxDataSize(const std::size_t max_data_size)