Skip to content

EntropyEngine::Core::Concurrency::WorkContractGroup

EntropyEngine::Core::Concurrency::WorkContractGroup

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

Factory and manager for work contracts with lock-free scheduling. More…

#include <WorkContractGroup.h>

Inherits from EntropyEngine::Core::EntropyObject

Name
using std::list< std::function< void()> >::iteratorCapacityCallback
Name
~WorkContractGroup()
Destructor ensures all work is stopped and completed.
voidwait()
Waits for all scheduled and executing contracts to complete.
ScheduleResultunscheduleContract(const WorkContractHandle & handle)
Removes a contract from scheduling (called by handle.unschedule()).
virtual std::stringtoString() const override
Human-readable short string (class@ptr by default).
voidstop()
Stops the group from accepting new work selections.
voidsetTimedDeferralCallback(std::function< size_t()> callback)
Sets a callback for checking timed deferrals.
voidsetConcurrencyProvider(IConcurrencyProvider * provider)
Associates this group with a concurrency provider.
WorkContractHandleselectForMainThreadExecution(std::optional< std::reference_wrapper< uint64_t > > bias =std::nullopt)
Selects a main thread scheduled contract for execution.
WorkContractHandleselectForExecution(std::optional< std::reference_wrapper< uint64_t > > bias =std::nullopt)
Selects a scheduled contract for execution.
size_tscheduledCount() const
Gets the number of contracts currently scheduled for execution.
ScheduleResultscheduleContract(const WorkContractHandle & handle)
Schedules a contract for execution (called by handle.schedule()).
voidresume()
Resumes the group to allow new work selections.
voidremoveOnCapacityAvailable(CapacityCallback it)
Remove a capacity available callback.
voidreleaseContract(const WorkContractHandle & handle)
Immediately releases a contract (called by handle.release()).
WorkContractGroup &operator=(const WorkContractGroup & ) =delete
WorkContractGroup &operator=(WorkContractGroup && other)
size_tmainThreadScheduledCount() const
Gets the number of main thread contracts currently scheduled.
size_tmainThreadExecutingCount() const
Gets the number of main thread contracts currently executing.
boolisValidHandle(const WorkContractHandle & handle) const
Validates a handle belongs to this group (called by handle.valid()).
boolisStopping() const
Checks if the group is in the process of stopping.
boolhasMainThreadWork() const
Checks if there are any main thread contracts ready to execute.
ContractStategetContractState(const WorkContractHandle & handle) const
Gets the current state of a contract.
IConcurrencyProvider *getConcurrencyProvider() const
Gets the currently associated concurrency provider.
size_texecutingCount() const
Returns the current number of contracts being actively executed.
size_texecuteMainThreadWork(size_t maxContracts)
Executes main thread targeted work contracts with a limit.
voidexecuteContract(const WorkContractHandle & handle)
Executes the work function of a contract.
size_texecuteAllMainThreadWork()
Executes all main thread targeted work contracts.
voidexecuteAllBackgroundWork()
Executes all background (non-main-thread) contracts sequentially in the calling thread.
virtual std::stringdescription() const override
Long-form description; defaults to toString().
virtual std::stringdebugString() const override
Debug-oriented string including refcount and handle when present.
WorkContractHandlecreateContract(std::function< void()> work, ExecutionType executionType =ExecutionType::AnyThread)
Creates a new work contract with the given work function.
voidcompleteMainThreadExecution(const WorkContractHandle & handle)
Completes execution and cleans up a main thread contract.
voidcompleteExecution(const WorkContractHandle & handle)
Completes execution and cleans up a contract.
virtual const char *className() const override
Runtime class name for diagnostics and reflection.
virtual uint64_tclassHash() const override
Stable type hash for cross-language identification.
size_tcheckTimedDeferrals()
Checks for timed deferrals and schedules ready nodes.
size_tcapacity() const
Gets the maximum capacity of this group.
CapacityCallbackaddOnCapacityAvailable(std::function< void()> callback)
Add a callback to be invoked when capacity becomes available.
size_tactiveCount() const
Gets the number of currently allocated contracts.
voidabortExecution(const WorkContractHandle & handle)
Aborts execution without running the task (shutdown-only path).
WorkContractGroup(size_t capacity, std::string name =“WorkContractGroup”)
Constructs a work contract group with specified capacity.
WorkContractGroup(const WorkContractGroup & ) =delete
WorkContractGroup(WorkContractGroup && other)

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.
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).
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.
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::WorkContractGroup;

Factory and manager for work contracts with lock-free scheduling.

WorkContractGroup implements a work dispatcher capable of managing thousands of tasks without locks or blocking operations. It provides a comprehensive pool of work contracts with allocation, scheduling, and execution primitives suitable for job systems, task graphs, and high-throughput work management scenarios.

The implementation uses SignalTree-based lock-free operations, enabling multiple threads to schedule and select work concurrently without contention. This design is optimized for game engines, parallel processing systems, and applications requiring management of numerous small work units.

Key features:

  • Lock-free contract scheduling and selection
  • Generation-based handles prevent use-after-free bugs
  • Immediate resource cleanup on completion
  • Statistical monitoring (active/scheduled counts)
  • Wait functionality for synchronization points

Important: This class provides scheduling primitives without handling parallel execution directly. External executors such as WorkService are required for concurrent work processing. The class functions as a centralized work registry where tasks are posted and claimed by worker threads.

Handle semantics:

  • WorkContractHandle derives from EntropyObject and is stamped with (owner + index + generation)
  • Copying a handle copies the stamped identity; validation is performed against this group’s slots
  • The group owns the lifetime; when a slot is released, the object’s identity is cleared and the generation is incremented to invalidate stale handles
// Complete workflow: mixed execution with worker service
WorkContractGroup group(1024);
WorkService service(4); // 4 worker threads
service.addWorkContractGroup(&group);
service.start();
// Submit background work
std::vector<WorkContractHandle> handles;
for (int i = 0; i < 10; ++i) {
auto handle = group.createContract([i]() {
processData(i);
});
handle.schedule();
handles.push_back(handle);
}
// Submit main thread work
auto uiHandle = group.createContract([]() {
updateProgressBar();
}, ExecutionType::MainThread);
uiHandle.schedule();
// Main thread pumps its work
while (group.hasMainThreadWork()) {
group.executeMainThreadWork(5); // Process up to 5 per frame
renderFrame();
}
// Wait for all background work to complete
group.wait();
service.stop();
using EntropyEngine::Core::Concurrency::WorkContractGroup::CapacityCallback = std::list<std::function<void()>>::iterator;
~WorkContractGroup()

Destructor ensures all work is stopped and completed.

Follows a strict destruction protocol to prevent deadlocks:

  1. Calls stop() to prevent new work selection
  2. Calls wait() to ensure all executing work completes
  3. Unschedules and releases all remaining contracts
  4. Reads concurrency provider pointer WITHOUT holding mutex lock
  5. Calls notifyGroupDestroyed() to inform provider of destruction

CRITICAL: The provider notification is made without holding the group’s concurrency provider mutex to prevent ABBA deadlock with WorkService. Any deviation from this protocol may result in deadlock during destruction.

The provider will then:

  • Remove this group from its internal lists
  • Call setConcurrencyProvider(nullptr) to clear the back-reference

This ensures proper bidirectional cleanup without lock ordering issues.

void wait()

Waits for all scheduled and executing contracts to complete.

Blocks until all work finishes. Includes scheduled and executing contracts.

// Submit a batch of work
for (int i = 0; i < 100; ++i) {
auto handle = group.createContract([i]() { processItem(i); });
handle.schedule();
}
// Wait for all work to complete
group.wait();
std::cout << "All work finished!\n";
ScheduleResult unscheduleContract(
const WorkContractHandle & handle
)

Removes a contract from scheduling (called by handle.unschedule()).

Parameters:

  • handle Handle to the contract to unschedule

Return: Result indicating success or failure reason

Removes from ready list if not yet executing. Use handle method instead.

virtual std::string toString() const override

Human-readable short string (class@ptr by default).

Reimplements: EntropyEngine::Core::EntropyObject::toString

void stop()

Stops the group from accepting new work selections.

Prevents new work selection. Executing work continues. Thread-safe.

void setTimedDeferralCallback(
std::function< size_t()> callback
)

Sets a callback for checking timed deferrals.

Parameters:

  • callback Function that checks and schedules timed deferrals, or nullptr to clear

Allows external owners (like WorkGraph) to provide timer functionality without requiring inheritance or RTTI/dynamic_cast. Thread-safe: Protected by mutex.

void setConcurrencyProvider(
IConcurrencyProvider * provider
)

Associates this group with a concurrency provider.

Parameters:

  • provider The concurrency provider to associate with, or nullptr to clear

Provider will be notified when work becomes available. Call during setup/teardown, not during active work execution.

WorkContractHandle selectForMainThreadExecution(
std::optional< std::reference_wrapper< uint64_t > > bias =std::nullopt
)

Selects a main thread scheduled contract for execution.

Parameters:

  • bias Optional selection bias for fair work distribution

Return: Handle to an executing contract, or invalid handle if none available

Use this from your main thread to pick up work that must run there. Typically called in a loop until no more work is available. Thread-safe with other selections.

// Main thread pump pattern
uint64_t bias = 0;
while (auto handle = group.selectForMainThreadExecution(std::ref(bias))) {
group.executeContract(handle);
group.completeMainThreadExecution(handle);
}
WorkContractHandle selectForExecution(
std::optional< std::reference_wrapper< uint64_t > > bias =std::nullopt
)

Selects a scheduled contract for execution.

Parameters:

  • bias Optional selection bias for fair work distribution

Return: Handle to an executing contract, or invalid handle if none available

Atomically transitions a contract from Scheduled to Executing state.

inline size_t scheduledCount() const

Gets the number of contracts currently scheduled for execution.

Return: Number of contracts currently scheduled and waiting for execution

if (group.scheduledCount() > 100) {
std::cout << "Work load is getting full - might want to throttle\n";
}
ScheduleResult scheduleContract(
const WorkContractHandle & handle
)

Schedules a contract for execution (called by handle.schedule()).

Parameters:

  • handle Handle to the contract to schedule

Return: Result indicating success or failure reason

Transitions a contract from Allocated to Scheduled state. Use the handle method instead of calling this directly.

void resume()

Resumes the group to allow new work selections.

Clears the stopping flag to allow selectForExecution() to return work again. Does NOT automatically notify waiting threads.

Thread-safe.

void removeOnCapacityAvailable(
CapacityCallback it
)

Remove a capacity available callback.

Parameters:

  • it Iterator returned from addOnCapacityAvailable
void releaseContract(
const WorkContractHandle & handle
)

Immediately releases a contract (called by handle.release()).

Parameters:

  • handle Handle to the contract to release

Forcibly frees a contract. Use the handle method instead.

WorkContractGroup & operator=(
const WorkContractGroup &
) =delete
WorkContractGroup & operator=(
WorkContractGroup && other
)
inline size_t mainThreadScheduledCount() const

Gets the number of main thread contracts currently scheduled.

Return: Number of main thread contracts waiting for execution

inline size_t mainThreadExecutingCount() const

Gets the number of main thread contracts currently executing.

Return: Number of main thread contracts being executed

bool isValidHandle(
const WorkContractHandle & handle
) const

Validates a handle belongs to this group (called by handle.valid()).

Parameters:

  • handle Handle to validate

Return: true if handle is valid and belongs to this group

Checks handle validity and generation. Use handle method instead.

inline bool isStopping() const

Checks if the group is in the process of stopping.

Return: true if stop() has been called, false otherwise

inline bool hasMainThreadWork() const

Checks if there are any main thread contracts ready to execute.

Return: true if main thread work is available

ContractState getContractState(
const WorkContractHandle & handle
) const

Gets the current state of a contract.

Parameters:

  • handle Handle to query

Return: Current state of the contract, or Free if handle is invalid

inline IConcurrencyProvider * getConcurrencyProvider() const

Gets the currently associated concurrency provider.

Return: The current provider, or nullptr if none is set

size_t executingCount() const

Returns the current number of contracts being actively executed.

Return: The number of currently executing contracts

Useful for thread scheduling and load balancing decisions.

size_t executeMainThreadWork(
size_t maxContracts
)

Executes main thread targeted work contracts with a limit.

Parameters:

  • maxContracts Maximum number of contracts to execute

Return: Number of contracts actually executed

Use when you need to bound main thread work per frame/iteration. Prevents blocking the main thread for too long. Must be called from the main thread.

// Limit main thread work to maintain 60 FPS
void gameLoop() {
// Execute at most 5 tasks per frame
size_t executed = group.executeMainThreadWork(5);
renderFrame();
}
void executeContract(
const WorkContractHandle & handle
)

Executes the work function of a contract.

Parameters:

  • handle Handle to the contract to execute (must be in Executing state)

Only call on contracts returned by selectForExecution().

size_t executeAllMainThreadWork()

Executes all main thread targeted work contracts.

Return: Number of contracts actually executed

Convenience method that handles the full pump cycle internally. Use this when you want to drain all main thread work at once. Must be called from the main thread.

// In your game loop or UI thread
void updateMainThread() {
size_t executed = group.executeAllMainThreadWork();
if (executed > 0) {
LOG_DEBUG("Processed {} main thread tasks", executed);
}
}
void executeAllBackgroundWork()

Executes all background (non-main-thread) contracts sequentially in the calling thread.

Grabs every scheduled background contract and executes them one by one in the current thread. Uses bias rotation to prevent starvation. Does NOT execute main thread targeted contracts.

// Schedule several background tasks
for (int i = 0; i < 10; ++i) {
auto handle = group.createContract([i]() {
std::cout << "Task " << i << "\n";
}); // Default is ExecutionType::AnyThread
handle.schedule();
}
// Execute all background contracts
group.executeAllBackgroundWork();
// All background tasks are now complete
virtual std::string description() const override

Long-form description; defaults to toString().

Reimplements: EntropyEngine::Core::EntropyObject::description

virtual std::string debugString() const override

Debug-oriented string including refcount and handle when present.

Reimplements: EntropyEngine::Core::EntropyObject::debugString

WorkContractHandle createContract(
std::function< void()> work,
ExecutionType executionType =ExecutionType::AnyThread
)

Creates a new work contract with the given work function.

Parameters:

  • work Function to execute when contract runs (should be thread-safe)
  • executionType Where this contract should be executed (default: AnyThread)

Return: Handle to the created contract, or invalid handle if group is full

// Simple work for any thread
auto handle = group.createContract([]() {
std::cout << "Hello from work thread!\n";
});
// Main thread targeted work
auto mainHandle = group.createContract([]() {
updateUI();
}, ExecutionType::MainThread);
// Check if creation succeeded
if (!handle.valid()) {
std::cerr << "Group is full - can't create more work\n";
}
void completeMainThreadExecution(
const WorkContractHandle & handle
)

Completes execution and cleans up a main thread contract.

Parameters:

  • handle Handle to the main thread contract that finished executing

Like completeExecution() but for main thread contracts. Updates the correct counters and frees the contract for reuse. Always call this after executeContract() for main thread work.

auto handle = group.selectForMainThreadExecution();
if (handle.valid()) {
group.executeContract(handle);
group.completeMainThreadExecution(handle); // Essential cleanup
}
void completeExecution(
const WorkContractHandle & handle
)

Completes execution and cleans up a contract.

Parameters:

  • handle Handle to the contract that finished executing

Must be called after executeContract() to complete the lifecycle.

inline virtual const char * className() const override

Runtime class name for diagnostics and reflection.

Reimplements: EntropyEngine::Core::EntropyObject::className

virtual uint64_t classHash() const override

Stable type hash for cross-language identification.

Reimplements: EntropyEngine::Core::EntropyObject::classHash

size_t checkTimedDeferrals()

Checks for timed deferrals and schedules ready nodes.

Return: Number of nodes that were scheduled from timed deferral queue

Invokes the timed deferral callback if one is set (used by WorkGraph for timer support). Returns 0 if no callback is registered (standard WorkContractGroups don’t support timers). Thread-safe: Protected by mutex.

inline size_t capacity() const

Gets the maximum capacity of this group.

Return: Maximum number of contracts this group can handle

CapacityCallback addOnCapacityAvailable(
std::function< void()> callback
)

Add a callback to be invoked when capacity becomes available.

Parameters:

  • callback Function to call when capacity is available

Return: Iterator that can be used to remove the callback

Called after a contract completes and frees up capacity.

inline size_t activeCount() const

Gets the number of currently allocated contracts.

Return: Number of contracts that have been created but not yet released

std::cout << "Using " << group.activeCount() << " of "
<< group.capacity() << " available slots\n";
void abortExecution(
const WorkContractHandle & handle
)

Aborts execution without running the task (shutdown-only path).

explicit WorkContractGroup(
size_t capacity,
std::string name ="WorkContractGroup"
)

Constructs a work contract group with specified capacity.

Parameters:

  • capacity Maximum number of contracts (typically 1024-8192)

Pre-allocates all data structures for lock-free operation. Choose capacity based on peak concurrent load.

// For a game engine handling frame tasks
WorkContractGroup frameWork(2048);
// For background processing
WorkContractGroup backgroundTasks(512);
WorkContractGroup(
const WorkContractGroup &
) =delete
WorkContractGroup(
WorkContractGroup && other
)

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