Skip to content

EntropyEngine::Core::Concurrency::WorkService

EntropyEngine::Core::Concurrency::WorkService

Section titled “EntropyEngine::Core::Concurrency::WorkService”

Thread pool service that executes work contracts from multiple groups. More…

#include <WorkService.h>

Inherits from EntropyEngine::Core::Concurrency::IConcurrencyProvider, EntropyEngine::Core::EntropyService, EntropyEngine::Core::EntropyObject

Name
structMainThreadWorkResult
Result structure for main thread work execution.
structConfig
Configuration parameters for the work service.
Name
enum classGroupOperationStatus { Removed = 1, OutOfSpace = 2, NotFound = 4, Exists = 3, Added = 0}
Name
~WorkService()
Destroys the work service and cleans up all resources.
voidwaitForStop()
Waits for all worker threads to finish (blocking).
virtual const char *version() const override
virtual voidunload() override
virtual TypeSystem::TypeIDtypeId() const override
virtual voidstop() override
Stops all worker threads and waits for them to finish.
virtual voidstart() override
Starts the worker threads and begins executing work.
size_tsetSoftFailureCount(size_t softFailureCount)
size_tsetFailureSleepTime(size_t failureSleepTime)
Sets how long the system will sleep a thread (in nanoseconds).
voidresetThreadLocalState()
Reset static thread-local variables (for testing only).
voidrequestStop()
Signals all worker threads to stop (non-blocking).
GroupOperationStatusremoveWorkContractGroup(WorkContractGroup * contractGroup)
Unregisters a work group from the service.
virtual voidnotifyWorkAvailable(WorkContractGroup * group =nullptr) override
Notifies the provider that work may be available.
virtual voidnotifyGroupDestroyed(WorkContractGroup * group) override
Called when a group is being destroyed.
virtual const char *name() const override
virtual voidload() override
boolisRunning() const
virtual const char *id() const override
boolhasMainThreadWork() const
Check if any registered group has main thread work available.
size_tgetWorkContractGroupCount() const
Gets the current work contract group count.
size_tgetThreadCount() const
The current thread count.
size_tgetSoftFailureCount() const
size_tgetFailureSleepTime() const
Gets how long the system will sleep a thread (in nanoseconds).
MainThreadWorkResultexecuteMainThreadWork(size_t maxContracts =std::numeric_limits< size_t >::max())
Execute main thread targeted work from all registered groups.
size_texecuteMainThreadWork(WorkContractGroup * group, size_t maxContracts =std::numeric_limits< size_t >::max())
Execute main thread work from a specific group.
virtual std::vector< TypeSystem::TypeID >dependsOnTypes() const override
virtual std::vector< std::string >dependsOn() const override
voidclear()
Removes all registered work groups (only when stopped).
GroupOperationStatusaddWorkContractGroup(WorkContractGroup * contractGroup)
Registers a work group with the service so it can be scheduled.
WorkService(Config config, std::unique_ptr< IWorkScheduler > scheduler =nullptr)
Creates a work service with the specified configuration.

Public Functions inherited from EntropyEngine::Core::Concurrency::IConcurrencyProvider

Name
virtual~IConcurrencyProvider() =default
virtual voidnotifyMainThreadWorkAvailable(WorkContractGroup * group =nullptr)
Notifies the provider that main thread work may be available.

Public Functions inherited from EntropyEngine::Core::EntropyService

Name
~EntropyService() override =default
ServiceStatestate() const
virtual const char *className() const override
Runtime class name for diagnostics and reflection.

Protected Functions inherited from EntropyEngine::Core::EntropyService

Name
voidsetState(ServiceState s)

Friends inherited from EntropyEngine::Core::EntropyService

Name
classEntropyServiceRegistry

Protected Classes inherited from EntropyEngine::Core::EntropyObject

Name
structHandleCore
Optional handle identity stamped by an owner/registry.

Public Functions inherited from EntropyEngine::Core::EntropyObject

Name
virtual~EntropyObject() =default
virtual const TypeSystem::TypeInfo *typeInfo() const
Optional richer type information; may be null.
booltryRetain() const
Attempts to retain only if the object is still alive.
virtual std::stringtoString() const
Human-readable short string (class@ptr by default).
voidretain() const
Increments the reference count.
voidrelease() const
Decrements the reference count and deletes when it reaches zero.
uint32_trefCount() const
Current reference count (approximate under contention).
EntropyObject &operator=(const EntropyObject & ) =delete
EntropyObject &operator=(EntropyObject && ) =delete
boolhasHandle() const
template <class OwnerT >
OwnerT *
handleOwnerAs() const
Returns the stamped owner pointer cast to the requested type.
const void *handleOwner() const
uint32_thandleIndex() const
uint64_thandleId() const
uint32_thandleGeneration() const
WeakControlBlock *getWeakControlBlock() const
Lazily retrieves or creates the weak control block.
virtual std::stringdescription() const
Long-form description; defaults to toString().
virtual std::stringdebugString() const
Debug-oriented string including refcount and handle when present.
virtual const char *className() const
Runtime class name for diagnostics and reflection.
virtual uint64_tclassHash() const
Stable type hash for cross-language identification.
EntropyObject() =default
EntropyObject(EntropyObject && ) =delete
EntropyObject(const EntropyObject & ) =delete

Protected Functions inherited from EntropyEngine::Core::EntropyObject

Name
void_setHandleIdentity(void * owner, uint32_t index, uint32_t generation)
void_clearHandleIdentity()

Protected Attributes inherited from EntropyEngine::Core::EntropyObject

Name
std::atomic< WeakControlBlock * >_weakBlock
Lazily allocated control block for weak refs.
std::atomic< uint32_t >_refCount
Thread-safe retain/release counter.
struct EntropyEngine::Core::EntropyObject::HandleCore_handle

Friends inherited from EntropyEngine::Core::EntropyObject

Name
structHandleAccess
class EntropyEngine::Core::Concurrency::WorkService;

Thread pool service that executes work contracts from multiple groups.

Think of WorkService as a team of workers (threads) that grab tasks from different departments (WorkContractGroups). Each department has its own queue of work, and the workers use a pluggable scheduler to decide which department needs help most.

The service delegates all scheduling decisions to an IWorkScheduler implementation, allowing you to experiment with different scheduling strategies without modifying the core thread management logic.

Perfect for:

  • Game engines that need to balance rendering, physics, AI, and audio work
  • Servers that handle different types of requests with varying priorities
  • Any system with multiple independent work producers that need fair execution

Key features:

  • Lock-free work execution (groups handle their own synchronization)
  • Pluggable scheduling strategies via IWorkScheduler interface
  • Thread pool management independent of scheduling logic
  • No work stealing between groups (intentional for data isolation)
// Create service with adaptive ranking scheduler (default)
WorkService::Config config;
config.threadCount = 8;
WorkService service(config);
// Or use a custom scheduler
auto scheduler = std::make_unique<RoundRobinScheduler>(schedulerConfig);
WorkService service(config, std::move(scheduler));
// Add work groups from different systems
service.addWorkContractGroup(&renderingGroup);
service.addWorkContractGroup(&physicsGroup);
service.addWorkContractGroup(&audioGroup);
// Start the workers
service.start();
// Systems submit work through their groups, service handles distribution
// ...
// Shutdown when done
service.stop();
EnumeratorValueDescription
Removed1
OutOfSpace2
NotFound4
Exists3
Added0
~WorkService()

Destroys the work service and cleans up all resources.

Automatically calls stop() if the service is still running, waits for all threads to finish, then cleans up. Safe to call even if service was never started.

void waitForStop()

Waits for all worker threads to finish (blocking).

Blocks the calling thread until all worker threads have completed execution. Should be called after requestStop() if you need to ensure all threads have finished before proceeding.

service.waitForStop(); // Wait for all threads to finish
inline virtual const char * version() const override

Reimplements: EntropyEngine::Core::EntropyService::version

inline virtual void unload() override

Reimplements: EntropyEngine::Core::EntropyService::unload

inline virtual TypeSystem::TypeID typeId() const override

Reimplements: EntropyEngine::Core::EntropyService::typeId

virtual void stop() override

Stops all worker threads and waits for them to finish.

Reimplements: EntropyEngine::Core::EntropyService::stop

Convenience method that calls requestStop() followed by waitForStop(). This is a blocking call that ensures all threads have completed before returning.

service.stop(); // Stop and wait for all threads (blocking)
virtual void start() override

Starts the worker threads and begins executing work.

Reimplements: EntropyEngine::Core::EntropyService::start

Spawns the configured number of worker threads, each running the adaptive scheduling algorithm. Safe to call multiple times - if already running, does nothing.

service.start(); // Workers now actively looking for work
size_t setSoftFailureCount(
size_t softFailureCount
)
size_t setFailureSleepTime(
size_t failureSleepTime
)

Sets how long the system will sleep a thread (in nanoseconds).

Parameters:

  • failureSleepTime How long to sleep a thread for (in nanoseconds).

Return: How long the system will now sleep a thread for (in nanoseconds).

static void resetThreadLocalState()

Reset static thread-local variables (for testing only).

Warning: This should only be used in test environments to ensure clean state between tests

void requestStop()

Signals all worker threads to stop (non-blocking).

Requests workers to stop without waiting. Returns immediately while workers complete their current contract before stopping.

service.requestStop(); // Signal workers to stop (non-blocking)
GroupOperationStatus removeWorkContractGroup(
WorkContractGroup * contractGroup
)

Unregisters a work group from the service.

Parameters:

  • contractGroup The group to remove - must be currently registered

Return: Removed if successful, NotFound if group wasn’t registered

Removes a group from the scheduling rotation. Any work already in the group remains there - this just stops the service from checking it for new work.

Important: This takes a lock and might block. Best practice is to remove groups during shutdown or when a system is being disabled, not during active execution.

// Clean up during system shutdown
service.removeWorkContractGroup(&physicsGroup);
virtual void notifyWorkAvailable(
WorkContractGroup * group =nullptr
) override

Notifies the provider that work may be available.

Parameters:

  • group The group that has new work available (optional, for routing)

Reimplements: EntropyEngine::Core::Concurrency::IConcurrencyProvider::notifyWorkAvailable

Called by WorkContractGroup when new work is scheduled. The provider should wake up any waiting threads to check for work. This is just a hint - the work may have already been consumed by the time a thread wakes up.

virtual void notifyGroupDestroyed(
WorkContractGroup * group
) override

Called when a group is being destroyed.

Parameters:

  • group The group being destroyed

Reimplements: EntropyEngine::Core::Concurrency::IConcurrencyProvider::notifyGroupDestroyed

Allows the provider to clean up any references to the group. After this call, the provider must not access the group pointer.

inline virtual const char * name() const override

Reimplements: EntropyEngine::Core::EntropyService::name

inline virtual void load() override

Reimplements: EntropyEngine::Core::EntropyService::load

bool isRunning() const
inline virtual const char * id() const override

Reimplements: EntropyEngine::Core::EntropyService::id

bool hasMainThreadWork() const

Check if any registered group has main thread work available.

Return: true if at least one group has main thread work scheduled

Quick non-blocking check to determine if you need to pump main thread work. Use this to avoid unnecessary calls to executeMainThreadWork().

// Only pump if there's work to do
if (service.hasMainThreadWork()) {
service.executeMainThreadWork(frameWorkBudget);
}
size_t getWorkContractGroupCount() const

Gets the current work contract group count.

Return: How many work contract groups that are currently registered in the work service.

This will return however many work gcontract groups that are currently registered in the work service. You should use this to check to make sure that there is room to actually register your work contract group, as the work service does not dynamically reallocate its pool of work contract groups.

This is intentional to better force right sizing of the group.

auto currentWorkGroupCount = workService.getWorkContractGroupCount();
if (currentWorkGroupCount < workService.getMaxWorkGroups() -1)
workService.addWorkContractGroup(&contractGroup);
size_t getThreadCount() const

The current thread count.

Return: The thread count.

Ranges from 1 to max concurrency.

size_t getSoftFailureCount() const
size_t getFailureSleepTime() const

Gets how long the system will sleep a thread (in nanoseconds).

Return: How many nanoseconds the system will sleep a thread for after all hard retries have been exhausted.

MainThreadWorkResult executeMainThreadWork(
size_t maxContracts =std::numeric_limits< size_t >::max()
)

Execute main thread targeted work from all registered groups.

Parameters:

  • maxContracts Maximum number of contracts to execute (default: unlimited)

Return: MainThreadWorkResult with execution statistics

Call from your main thread to process UI, rendering, or other main-thread-only work. Distributes execution fairly across groups. Use maxContracts to limit work per frame and maintain responsiveness.

// Game loop with frame budget
void gameUpdate() {
// Process up to 10 main thread tasks per frame
auto result = service.executeMainThreadWork(10);
if (result.moreWorkAvailable) {
// More work pending - will process next frame
needsUpdate = true;
}
// Continue with rendering
render();
}
size_t executeMainThreadWork(
WorkContractGroup * group,
size_t maxContracts =std::numeric_limits< size_t >::max()
)

Execute main thread work from a specific group.

Parameters:

  • group The group to execute work from
  • maxContracts Maximum number of contracts to execute

Return: Number of contracts executed

Use when you need fine-grained control over which group’s work executes. Useful for prioritizing certain subsystems over others.

// Prioritize UI work over other main thread tasks
size_t uiWork = service.executeMainThreadWork(&uiGroup, 5);
size_t otherWork = service.executeMainThreadWork(&miscGroup, 2);
inline virtual std::vector< TypeSystem::TypeID > dependsOnTypes() const override

Reimplements: EntropyEngine::Core::EntropyService::dependsOnTypes

inline virtual std::vector< std::string > dependsOn() const override

Reimplements: EntropyEngine::Core::EntropyService::dependsOn

void clear()

Removes all registered work groups (only when stopped).

Nuclear option that unregisters all groups at once. Only works when the service is stopped to prevent race conditions. Mainly useful for testing or complete system resets.

service.stop();
service.clear(); // All groups unregistered
// Re-add groups and restart...
GroupOperationStatus addWorkContractGroup(
WorkContractGroup * contractGroup
)

Registers a work group with the service so it can be scheduled.

Parameters:

  • contractGroup Pointer to the group to add - must remain valid while registered

Return: Added if successful, OutOfSpace if at capacity, Exists if already registered

Think of this as adding a new department to your worker pool. Once registered, the group will be included in the ranking algorithm and its work will be executed by the service’s threads.

Important: This takes a lock and might block briefly. Best practice is to register all your groups during initialization, not during active execution. The service pre-allocates space for maxWorkGroups to avoid resizing.

// Register groups during startup
if (service.addWorkContractGroup(&physicsGroup) != GroupOperationStatus::Added) {
LOG_ERROR("Failed to register physics work group");
}
explicit WorkService(
Config config,
std::unique_ptr< IWorkScheduler > scheduler =nullptr
)

Creates a work service with the specified configuration.

Parameters:

The service is created in a stopped state. You must call start() to begin executing work. Thread count is clamped to hardware concurrency, so asking for 1000 threads on an 8-core machine gets you 8 threads.

Uses AdaptiveRankingScheduler by default if no scheduler is provided.

// Use default adaptive ranking scheduler
WorkService::Config config;
config.threadCount = 0; // Use all CPU cores
WorkService service(config);
// Or provide a custom scheduler
auto scheduler = std::make_unique<RoundRobinScheduler>(schedulerConfig);
WorkService service(config, std::move(scheduler));

Updated on 2026-01-26 at 16:50:32 -0500