Workers

Purpose

Workers shares CPU execution capacity between workload groups. Groups define admission, concurrency, relative scheduling share and optional CPU/locality policy without creating another physical pool per workload.

Interface

Types below are in CE::Workers unless another namespace is shown.

Types and operations

API and purpose

Pool, PoolOptions
Own reusable CPU capacity, optionally informed by discovered topology. Pool lifetime; capacity.

Group, GroupOptions
Submit owned jobs with concurrency caps, scheduling share and CPU policy; close then drain. Jobs; CPU policy.

TopologyProvider, Topology
Supply/inspect native CPU, core and cache observations. Topology providers; adapter catalogue.

Configuration
Validate typed pool/group configuration; optional Workers integration loads JSON. Typed and JSON configuration; Workers module.

GroupPolicy, GroupStatus, Capabilities
Compare requested/effective policy, activity and supported native behavior. CPU policy; worker statistics.

CPU policy enums

Type

Choices and details

CpuPolicy

Explicit eligible CPU IDs, strength, locality and optional cache-domain selection. CPU policy and capabilities.

PolicyStrength

Preferred may report fallback; unsupported Required policy rejects. Required and preferred policy.

LocalityPolicy

Automatic, Reuse, Spread within reported topology and binding support. Locality selection.

CacheType

Data, Instruction, Unified classify discovered cache domains. Topology snapshots.

Usage

For a running game, create a tracked group through engine.make_worker_group(options). Submit CPU-only work with owned inputs and retain its future. Runtime settles these groups during shutdown while accepted platform dependencies can still progress.

        ---
config:
  theme: base
  themeVariables:
    fontFamily: system-ui, sans-serif
    fontSize: 14px
  flowchart:
    htmlLabels: true
    curve: linear
    nodeSpacing: 22
    rankSpacing: 38
    padding: 12
---
flowchart LR
    accTitle: Workers · simple Context-owned groups
    accDescr: Local names omit Workers::; ambiguous nested namespaces remain qualified. Arrows point toward dependencies. Application roles are rounded; named API operations have rounded corners. Dashed arrows mark optional choices.
    k0(["Application<br/>composition"]):::app
    k1["Runtime::<br/>ExecutionOptions"]:::neighbor
    k2["Runtime::<br/>Context"]:::neighbor
    k3["Pool"]:::focal
    k4["Group"]:::focal
    k5(["Application<br/>CPU preparation"]):::app
    k6["GroupOptions"]:::focal
    k0 --> k1
    k2 --> k1
    k2 --> k3
    k2 --> k4
    k5 --> k4
    k4 --> k6
    classDef focal fill:#e7f3ef,stroke:#387d6b,color:#153d33,stroke-width:1.2px
    classDef neighbor fill:#f0f3f8,stroke:#8c9bb1,color:#35445b,stroke-width:1px
    classDef app fill:#fff2df,stroke:#c09452,color:#674515,stroke-dasharray:5 3
    classDef system fill:#eaf0fc,stroke:#617fab,color:#263f69,stroke-width:1.2px
    linkStyle default stroke:#7e8fa7,stroke-width:1.2px

    

Local names omit Workers:: where unambiguous. Explore this diagram and its other views →

Explore application preparation, a pool shared by contexts, or worker implementation dependencies.

Choose pool and group ownership

Situation

Construction path

Who closes and drains it?

One game’s ordinary CPU preparation

Set Runtime::ExecutionOptions, then Context::make_worker_group()

Runtime/Context tracks every resulting group. Owned physical capacity is created lazily; the default is one worker.

Several contexts or services share physical capacity

Supply ExecutionOptions::shared_pool, then create each context’s groups

Each context closes/drains its groups; the application owns the pool and unrelated groups.

A service runs independently of Runtime

Own Workers::Pool and call pool.make_group()

The service stops producers, closes acceptance and services dependencies before drain/join.

CPU/cache policy needs native topology

Set Workers::PoolOptions with a retained Workers::TopologyProvider

The pool retains the provider through worker joins. The optional Workers module supplies hwloc discovery.

Separate capacity from scheduling policy

Setting

Effect

Decision to make

Pool worker count

Physical CPU threads shared by all groups

Choose total capacity once. Context’s owned count must be positive; standalone PoolOptions supports explicit automatic capacity with zero.

GroupOptions::max_concurrency

Running-job cap, limited to pool capacity; zero uses that capacity

Limit a workload without allocating extra threads.

weight and priority

Relative selection share among eligible groups

Balance queued work; neither setting guarantees latency, completion order or OS priority.

GroupOptions::cpu

Eligible CPUs and optional locality/cache policy

Choose Required rejection or Preferred fallback, then inspect effective policy/capabilities.

A job’s future carries its result or failure. A closed group’s drain() also waits for accepted jobs and capture release; status counters alone are not that barrier. The worker contract defines scheduling and policy limits.

Uses

Dependency

Workers’ use

Workers::TopologyProvider, Workers::Topology

Optional startup discovery and supported CPU policy application; the neutral pool does not implicitly load a provider.

Workers::CpuPolicy

Validate requested eligibility/locality against pool capabilities and verify required native policy before callback entry.

Diagnostics and Logging

Identify groups/pools and expose queue, callback and policy observations.

Graphics follow-up belongs to the application’s job workflow through Dispatch. The pool itself supplies CPU execution and has no owner-dispatch dependency.

Used by

Consumer

Relationship to the pool

Runtime::Context

Create tracked groups on lazy owned or supplied shared capacity. Context ownership.

Events::worker_delivery()

Execute one serial listener stream over a group; separate adapters can run concurrently. Event delivery.

Text::Rendering::Updater

Create a Context-tracked serial preparation group, submit owned text requests, then upload through platform dispatch. Text updates.

Application asset or audio preparation

Schedule independent CPU operations and retain their results; these services do not automatically submit preparation jobs.

Standalone application service

Share physical capacity without constructing a game session.

Contracts

Jobs own callables and settle futures with results/errors. FIFO selection does not imply completion order when concurrency exceeds one. Caps limit running jobs; weight/priority determine selection share rather than OS scheduling priority. CPU eligibility is not reserved core ownership or guaranteed speedup.

Do not block a job on child futures from the same saturated pool. Close before drain; drainage waits for accepted work and capture release. Same-pool drainage and self-join reject. Required unsupported native policy rejects while preferred policy may report fallback.

Lifecycle

Create a pool and groups, submit owned work, close acceptance and drain before destroying borrowed dependencies. Pool destruction closes, drains and joins; saved group handles reject safely after pool disappearance. Thread-start failure joins threads already created. Context-created groups close with that context; unrelated groups in an injected shared pool remain open.

Execution

Physical workers execute eligible groups according to scheduling policy. They are separate from runtime’s dedicated concurrent simulation thread. Graphics work still needs platform dispatch. During runtime shutdown, accepted CPU work settles while platform dependencies can continue making progress.

Integration

Before context creation, select the desired physical capacity through ExecutionOptions; its default is one worker. During game initialization, retain a context-owned preparation group:

auto preparation = engine_.make_worker_group({.max_concurrency = 2});
auto result = preparation.submit([] { return 6 * 7; });

Real preparation should return owned CPU data. Retain/read its future and send completed results to platform for upload; the group’s cap cannot exceed physical pool capacity and does not create two additional threads. To permit two jobs to run at once, the configured pool must contain at least two workers.

Limits and further reading

No work stealing, forced termination, live pool reconfiguration or NUMA memory placement is implied. Topology discovery does not prove a performance benefit. Read worker contracts and optional integration, runtime shutdown and pool/group declarations.