|
Foundation
|
Atomic, lock-free Thread Pool implementation with fixed bounds. More...
#include <ThreadPool.hpp>
Public Member Functions | |
| ThreadPool (size_t numThreads, size_t maxTasks, Allocator *alloc, StringView name="ThreadPool") | |
| Construct a thread pool with the given number of worker threads. | |
| template<typename T , typename... Args> requires std::is_base_of_v<Job, T> | |
| T * | PushImpl (JobPriority priority, Args &&... args) |
| Push a job implementing ThreadPoolJob to the thread pool. | |
| template<typename T , typename... Args> requires std::is_base_of_v<Job, T> | |
| T * | PushImpl (Args &&... args) |
| template<typename T , typename... Args> requires std::is_base_of_v<Job, T> | |
| T * | PushImplAlloc (Allocator *jobAllocator, Args &&... args) |
| Push a job with an explicit allocator for the job object. | |
| template<typename T , typename... Args> requires std::is_base_of_v<Job, T> | |
| T * | PushImplAlloc (JobPriority priority, Allocator *jobAllocator, Args &&... args) |
| template<typename Lambda , typename... Args> | |
| auto | Push (JobPriority priority, Lambda &&func, Args const &... args) |
| Push a lambda job to the thread pool. | |
| template<typename Lambda , typename... Args> | |
| auto | Push (Lambda &&func, Args const &... args) |
| template<typename Lambda , typename... Args> | |
| auto | PushAlloc (Allocator *jobAllocator, Lambda &&func, Args const &... args) |
| Push a lambda job with an explicit allocator for the job object. | |
| template<typename Lambda , typename... Args> | |
| auto | PushAlloc (JobPriority priority, Allocator *jobAllocator, Lambda &&func, Args const &... args) |
| void | CoInvoke (Job &job, size_t count, JobPriority priority=JobPriority::Normal) |
Enqueues count non-owning references to a single, externally-owned job so that up to count workers co-run it concurrently (each call to ThreadPoolJob::Execute receives that worker's id). The pool never destroys or frees job — the caller owns its lifetime and MUST keep it alive until all co-invocations complete (e.g. via a fork-join latch). Used by ParallelFor for a self-draining, zero-allocation job. | |
| size_t | GetWorkerCount () const noexcept |
| Number of worker threads. Worker ids passed to Execute are in [0, this). | |
| size_t | GetParallelForConcurrency () const noexcept |
| Number of distinct worker ids a ParallelFor functor may see (workers + the participating caller). Size per-worker scratch to this. | |
| template<typename Fn > | |
| void | ParallelFor (ExecutionPolicy policy, size_t count, Fn &&fn) |
Parallel-for over [0, count): invokes fn for every index as fn(i) or fn(i, workerId) (the worker id is passed only if the functor accepts it), blocking until all indices are processed. | |
| template<typename Fn > | |
| void | ParallelFor (size_t count, Fn &&fn) |
| Index parallel-for defaulting to ExecutionPolicy::Par. | |
| template<typename It , typename Fn > requires std::random_access_iterator<It> | |
| void | ParallelFor (ExecutionPolicy policy, It first, It last, Fn &&fn) |
Iterator-range parallel-for, like a (policy-aware) parallel std::for_each: invokes fn for each element in [first, last) as fn(elem) or fn(elem, workerId). | |
| template<typename It , typename Fn > requires std::random_access_iterator<It> | |
| void | ParallelFor (It first, It last, Fn &&fn) |
| Iterator-range parallel-for defaulting to ExecutionPolicy::Par. | |
| template<typename Fn > | |
| Future< void > | ParallelForAsync (ExecutionPolicy policy, size_t count, Fn &&fn) |
Non-blocking parallel-for over [0, count): schedules the work across the pool's workers and returns immediately with a Future<void> that becomes ready once every index has been processed. The calling thread does NOT participate. | |
| template<typename Fn > | |
| Future< void > | ParallelForAsync (size_t count, Fn &&fn) |
| Index async parallel-for defaulting to ExecutionPolicy::Par. | |
| template<typename It , typename Fn > requires std::random_access_iterator<It> | |
| Future< void > | ParallelForAsync (ExecutionPolicy policy, It first, It last, Fn &&fn) |
| Iterator-range overload of ParallelForAsync (random-access iterators only). | |
| template<typename It , typename Fn > requires std::random_access_iterator<It> | |
| Future< void > | ParallelForAsync (It first, It last, Fn &&fn) |
| Iterator-range async parallel-for defaulting to ExecutionPolicy::Par. | |
| void | Shutdown () |
| Shutdown the ThreadPool, potentially cancelling all pending jobs. | |
| void | Join () |
| Wait for all scheduled jobs to complete. | |
| ~ThreadPool () | |
| Shutdown, without waiting for pending jobs. | |
| size_t | GetPendingJobCount () const noexcept |
| size_t | GetCompletedJobCount () const noexcept |
| size_t | GetTotalJobCount () const noexcept |
Static Public Member Functions | |
| static Future< void > | MakeReadyFuture () |
Returns an already-satisfied Future<void> (for trivial/inline async paths). | |
| static const size_t | CalcTaskSize (size_t size) |
Private Member Functions | |
| void | ThreadPoolWorker (size_t id) |
| template<typename T , typename... Args> requires std::is_base_of_v<Job, T> | |
| T * | PushImplInternal (JobPriority priority, Allocator *jobAllocator, Args &&... args) |
| template<typename Lambda , typename... Args> | |
| auto | PushLambdaInternal (JobPriority priority, Allocator *jobAllocator, Lambda &&func, Args const &... args) |
Static Private Member Functions | |
| static constexpr size_t | PriorityIndex (JobPriority priority) noexcept |
Private Attributes | |
| Allocator * | mAllocator |
| String | mName |
| Atomic< bool > | mShutdown {false} |
| Atomic< size_t > | mComplete {0} |
| Atomic< size_t > | mTotal {0} |
| JobQueues | mJobs |
| Vector< Thread > | mThreads |
Atomic, lock-free Thread Pool implementation with fixed bounds.
| Foundation::Core::ThreadPool::ThreadPool | ( | size_t | numThreads, |
| size_t | maxTasks, | ||
| Allocator * | alloc, | ||
| StringView | name = "ThreadPool" |
||
| ) |
Construct a thread pool with the given number of worker threads.
| numThreads | Number of worker threads to spawn. |
| maxTasks | Max number of tasks that can be queued per priority. Must be a power of two - see getTaskSize |
| alloc | Allocator to use for internal and job allocations |
| name | Prefix for worker thread names ("name@id") |
| Foundation::Core::ThreadPool::~ThreadPool | ( | ) |
Shutdown, without waiting for pending jobs.
Aligns a number to upper, closest power of 2 so that it's a valid maxTasks size.
| void Foundation::Core::ThreadPool::CoInvoke | ( | Job & | job, |
| size_t | count, | ||
| JobPriority | priority = JobPriority::Normal |
||
| ) |
Enqueues count non-owning references to a single, externally-owned job so that up to count workers co-run it concurrently (each call to ThreadPoolJob::Execute receives that worker's id). The pool never destroys or frees job — the caller owns its lifetime and MUST keep it alive until all co-invocations complete (e.g. via a fork-join latch). Used by ParallelFor for a self-draining, zero-allocation job.
count is typically <= GetWorkerCount; worker ids passed to Execute are in [0, GetWorkerCount). A participating caller should use id == GetWorkerCount.
|
inlinenoexcept |
|
inlinenoexcept |
Number of distinct worker ids a ParallelFor functor may see (workers + the participating caller). Size per-worker scratch to this.
|
inlinenoexcept |
|
inlinenoexcept |
|
inlinenoexcept |
Number of worker threads. Worker ids passed to Execute are in [0, this).
| void Foundation::Core::ThreadPool::Join | ( | ) |
Wait for all scheduled jobs to complete.
Returns an already-satisfied Future<void> (for trivial/inline async paths).
|
inline |
Iterator-range parallel-for, like a (policy-aware) parallel std::for_each: invokes fn for each element in [first, last) as fn(elem) or fn(elem, workerId).
A thin wrapper over the index form (random-access iterators only): the element is passed by reference, so fn may mutate it (parallel transform). Same concurrency contract as the index form; policy selects serial vs parallel at runtime.
|
inline |
Parallel-for over [0, count): invokes fn for every index as fn(i) or fn(i, workerId) (the worker id is passed only if the functor accepts it), blocking until all indices are processed.
Main-thread-initiated and non-nesting. With ExecutionPolicy::Par it reuses the pool's workers and the calling thread (which participates with id == GetWorkerCount), pushing only non-owning references to a stack-resident ParallelForJob — zero per-call allocation, no futures. ExecutionPolicy::Seq (and a worker-less pool) runs inline in order. fn must be safe to invoke concurrently under Par; key any scratch by workerId. Granularity is one index per call: a job that wants coarser work batches itself by choosing count.
Iterator-range parallel-for defaulting to ExecutionPolicy::Par.
Index parallel-for defaulting to ExecutionPolicy::Par.
|
inline |
Iterator-range overload of ParallelForAsync (random-access iterators only).
Owns both first and fn in the scheduled functor (the caller's stack frame may unwind before the work runs), then forwards to the index form.
|
inline |
Non-blocking parallel-for over [0, count): schedules the work across the pool's workers and returns immediately with a Future<void> that becomes ready once every index has been processed. The calling thread does NOT participate.
Unlike ParallelFor (which blocks and lets the caller help), this lets the caller schedule deformation/skinning work and continue with independent CPU work, then wait on the returned future only at the real dependency boundary. The functor is moved into a heap-resident ParallelForAsyncState shared by the pushed worker jobs; the last worker to finish satisfies the promise and frees the state. fn must be safe to invoke concurrently (same contract as ParallelFor); key any scratch by workerId, which is in [0, GetWorkerCount). With ExecutionPolicy::Seq, an empty range, or a worker-less pool the work runs inline on the calling thread and the returned future is already ready.
|
inline |
Iterator-range async parallel-for defaulting to ExecutionPolicy::Par.
Index async parallel-for defaulting to ExecutionPolicy::Par.
|
inlinestaticconstexprprivatenoexcept |
|
inline |
Push a lambda job to the thread pool.
|
inline |
Push a lambda job with an explicit allocator for the job object.
| jobAllocator | Optional allocator for the job object. If null, the thread pool allocator is used. |
|
inline |
|
inline |
Push a job implementing ThreadPoolJob to the thread pool.
|
inline |
Push a job with an explicit allocator for the job object.
| jobAllocator | Optional allocator for the job object. If null, the thread pool allocator is used. |
|
inline |
|
inlineprivate |
|
inlineprivate |
| void Foundation::Core::ThreadPool::Shutdown | ( | ) |
Shutdown the ThreadPool, potentially cancelling all pending jobs.
|
private |
|
private |
|
private |